* calendar/todo-mode.el: Fix two bugs.
[bpt/emacs.git] / src / xdisp.c
index 1513d4a..1106e6f 100644 (file)
@@ -98,7 +98,9 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
       This function attempts to redisplay a window by reusing parts of
       its existing display.  It finds and reuses the part that was not
-      changed, and redraws the rest.
+      changed, and redraws the rest.  (The "id" part in the function's
+      name stands for "insert/delete", not for "identification" or
+      somesuch.)
 
     . try_window
 
@@ -113,6 +115,19 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
    optimizations were successful, redisplay calls redisplay_windows,
    which performs a full redisplay of all windows.
 
+   Note that there's one more important optimization up Emacs's
+   sleeve, but it is related to actually redrawing the potentially
+   changed portions of the window/frame, not to reproducing the
+   desired matrices of those potentially changed portions.  Namely,
+   the function update_frame and its subroutines, which you will find
+   in dispnew.c, compare the desired matrices with the current
+   matrices, and only redraw the portions that changed.  So it could
+   happen that the functions in this file for some reason decide that
+   the entire desired matrix needs to be regenerated from scratch, and
+   still only parts of the Emacs display, or even nothing at all, will
+   be actually delivered to the glass, because update_frame has found
+   that the new and the old screen contents are similar or identical.
+
    Desired matrices.
 
    Desired matrices are always built per Emacs window.  The function
@@ -1262,12 +1277,23 @@ Value is the height in pixels of the line at point.  */)
   struct it it;
   struct text_pos pt;
   struct window *w = XWINDOW (selected_window);
+  struct buffer *old_buffer = NULL;
+  Lisp_Object result;
 
+  if (XBUFFER (w->contents) != current_buffer)
+    {
+      old_buffer = current_buffer;
+      set_buffer_internal_1 (XBUFFER (w->contents));
+    }
   SET_TEXT_POS (pt, PT, PT_BYTE);
   start_display (&it, w, pt);
   it.vpos = it.current_y = 0;
   last_height = 0;
-  return make_number (line_bottom_y (&it));
+  result = make_number (line_bottom_y (&it));
+  if (old_buffer)
+    set_buffer_internal_1 (old_buffer);
+
+  return result;
 }
 
 /* Return the default pixel height of text lines in window W.  The
@@ -2580,8 +2606,8 @@ safe_eval_handler (Lisp_Object arg, ptrdiff_t nargs, Lisp_Object *args)
    following.  Return the result, or nil if something went
    wrong.  Prevent redisplay during the evaluation.  */
 
-Lisp_Object
-safe_call (ptrdiff_t nargs, Lisp_Object func, ...)
+static Lisp_Object
+safe__call (bool inhibit_quit, ptrdiff_t nargs, Lisp_Object func, va_list ap)
 {
   Lisp_Object val;
 
@@ -2589,21 +2615,20 @@ safe_call (ptrdiff_t nargs, Lisp_Object func, ...)
     val = Qnil;
   else
     {
-      va_list ap;
       ptrdiff_t i;
       ptrdiff_t count = SPECPDL_INDEX ();
       struct gcpro gcpro1;
       Lisp_Object *args = alloca (nargs * word_size);
 
       args[0] = func;
-      va_start (ap, func);
       for (i = 1; i < nargs; i++)
        args[i] = va_arg (ap, Lisp_Object);
-      va_end (ap);
 
       GCPRO1 (args[0]);
       gcpro1.nvars = nargs;
       specbind (Qinhibit_redisplay, Qt);
+      if (inhibit_quit)
+       specbind (Qinhibit_quit, Qt);
       /* Use Qt to ensure debugger does not run,
         so there is no possibility of wanting to redisplay.  */
       val = internal_condition_case_n (Ffuncall, nargs, args, Qt,
@@ -2615,6 +2640,17 @@ safe_call (ptrdiff_t nargs, Lisp_Object func, ...)
   return val;
 }
 
+Lisp_Object
+safe_call (ptrdiff_t nargs, Lisp_Object func, ...)
+{
+  Lisp_Object retval;
+  va_list ap;
+
+  va_start (ap, func);
+  retval = safe__call (false, nargs, func, ap);
+  va_end (ap);
+  return retval;
+}
 
 /* Call function FN with one argument ARG.
    Return the result, or nil if something went wrong.  */
@@ -2625,12 +2661,30 @@ safe_call1 (Lisp_Object fn, Lisp_Object arg)
   return safe_call (2, fn, arg);
 }
 
+static Lisp_Object
+safe__call1 (bool inhibit_quit, Lisp_Object fn, ...)
+{
+  Lisp_Object retval;
+  va_list ap;
+
+  va_start (ap, fn);
+  retval = safe__call (inhibit_quit, 2, fn, ap);
+  va_end (ap);
+  return retval;
+}
+
 static Lisp_Object Qeval;
 
 Lisp_Object
 safe_eval (Lisp_Object sexpr)
 {
-  return safe_call1 (Qeval, sexpr);
+  return safe__call1 (false, Qeval, sexpr);
+}
+
+static Lisp_Object
+safe__eval (bool inhibit_quit, Lisp_Object sexpr)
+{
+  return safe__call1 (inhibit_quit, Qeval, sexpr);
 }
 
 /* Call function FN with two arguments ARG1 and ARG2.
@@ -8517,7 +8571,7 @@ move_it_in_display_line_to (struct it *it,
        }
       else
        {
-         if (it->line_wrap == WORD_WRAP)
+         if (it->line_wrap == WORD_WRAP && it->area == TEXT_AREA)
            {
              if (IT_DISPLAYING_WHITESPACE (it))
                may_wrap = 1;
@@ -8651,7 +8705,12 @@ move_it_in_display_line_to (struct it *it,
                         doesn't fit on the line, e.g. a wide image.  */
                      it->hpos == 0
                      || (new_x == it->last_visible_x
-                         && FRAME_WINDOW_P (it->f)))
+                         && FRAME_WINDOW_P (it->f)
+                         /* When word-wrap is ON and we have a valid
+                            wrap point, we don't allow the last glyph
+                            to "just barely fit" on the line.  */
+                         && (it->line_wrap != WORD_WRAP
+                             || wrap_it.sp < 0)))
                    {
                      ++it->hpos;
                      it->current_x = new_x;
@@ -8796,8 +8855,11 @@ move_it_in_display_line_to (struct it *it,
                  if (closest_pos < ZV)
                    {
                      RESTORE_IT (it, &ppos_it, ppos_data);
-                     move_it_in_display_line_to (it, closest_pos, -1,
-                                                 MOVE_TO_POS);
+                     /* Don't recurse if closest_pos is equal to
+                        to_charpos, since we have just tried that.  */
+                     if (closest_pos != to_charpos)
+                       move_it_in_display_line_to (it, closest_pos, -1,
+                                                   MOVE_TO_POS);
                      result = MOVE_POS_MATCH_OR_ZV;
                    }
                  else
@@ -8858,8 +8920,9 @@ move_it_in_display_line_to (struct it *it,
                      && !at_eob_p && closest_pos < ZV)
                    {
                      RESTORE_IT (it, &ppos_it, ppos_data);
-                     move_it_in_display_line_to (it, closest_pos, -1,
-                                                 MOVE_TO_POS);
+                     if (closest_pos != to_charpos)
+                       move_it_in_display_line_to (it, closest_pos, -1,
+                                                   MOVE_TO_POS);
                    }
                  result = MOVE_POS_MATCH_OR_ZV;
                  break;
@@ -8877,7 +8940,9 @@ move_it_in_display_line_to (struct it *it,
              if (closest_pos < ZV)
                {
                  RESTORE_IT (it, &ppos_it, ppos_data);
-                 move_it_in_display_line_to (it, closest_pos, -1, MOVE_TO_POS);
+                 if (closest_pos != to_charpos)
+                   move_it_in_display_line_to (it, closest_pos, -1,
+                                               MOVE_TO_POS);
                }
              result = MOVE_POS_MATCH_OR_ZV;
              break;
@@ -9506,6 +9571,7 @@ move_it_by_lines (struct it *it, ptrdiff_t dvpos)
       ptrdiff_t start_charpos, i;
       int nchars_per_row
        = (it->last_visible_x - it->first_visible_x) / FRAME_COLUMN_WIDTH (it->f);
+      bool hit_pos_limit = false;
       ptrdiff_t pos_limit;
 
       /* Start at the beginning of the screen line containing IT's
@@ -9522,8 +9588,11 @@ move_it_by_lines (struct it *it, ptrdiff_t dvpos)
        pos_limit = BEGV;
       else
        pos_limit = max (start_charpos + dvpos * nchars_per_row, BEGV);
+
       for (i = -dvpos; i > 0 && IT_CHARPOS (*it) > pos_limit; --i)
        back_to_previous_visible_line_start (it);
+      if (i > 0 && IT_CHARPOS (*it) <= pos_limit)
+       hit_pos_limit = true;
       reseat (it, it->current.pos, 1);
 
       /* Move further back if we end up in a string or an image.  */
@@ -9567,6 +9636,25 @@ move_it_by_lines (struct it *it, ptrdiff_t dvpos)
          else
            bidi_unshelve_cache (it2data, 1);
        }
+      else if (hit_pos_limit && pos_limit > BEGV
+              && dvpos < 0 && it2.vpos < -dvpos)
+       {
+         /* If we hit the limit, but still didn't make it far enough
+            back, that means there's a display string with a newline
+            covering a large chunk of text, and that caused
+            back_to_previous_visible_line_start try to go too far.
+            Punish those who commit such atrocities by going back
+            until we've reached DVPOS, after lifting the limit, which
+            could make it slow for very long lines.  "If it hurts,
+            don't do that!"  */
+         dvpos += it2.vpos;
+         RESTORE_IT (it, it, it2data);
+         for (i = -dvpos; i > 0; --i)
+           {
+             back_to_previous_visible_line_start (it);
+             it->vpos--;
+           }
+       }
       else
        RESTORE_IT (it, it, it2data);
     }
@@ -9595,19 +9683,23 @@ character.  TO, if non-nil, specifies the last text position and
 defaults to the maximum accessible position of the buffer.  If TO is t,
 use the maximum accessible position that is not a newline character.
 
-The optional argument X_LIMIT, if non-nil, specifies the maximum text
-width that can be returned.  X_LIMIT nil or omitted, means to use the
+The optional argument X-LIMIT, if non-nil, specifies the maximum text
+width that can be returned.  X-LIMIT nil or omitted, means to use the
 pixel-width of WINDOW's body; use this if you do not intend to change
 the width of WINDOW.  Use the maximum width WINDOW may assume if you
-intend to change WINDOW's width.
+intend to change WINDOW's width.  In any case, text whose x-coordinate
+is beyond X-LIMIT is ignored.  Since calculating the width of long lines
+can take some time, it's always a good idea to make this argument as
+small as possible; in particular, if the buffer contains long lines that
+shall be truncated anyway.
 
-The optional argument Y_LIMIT, if non-nil, specifies the maximum text
+The optional argument Y-LIMIT, if non-nil, specifies the maximum text
 height that can be returned.  Text lines whose y-coordinate is beyond
-Y_LIMIT are ignored.  Since calculating the text height of a large
+Y-LIMIT are ignored.  Since calculating the text height of a large
 buffer can take some time, it makes sense to specify this argument if
 the size of the buffer is unknown.
 
-Optional argument MODE_AND_HEADER_LINE nil or omitted means do not
+Optional argument MODE-AND-HEADER-LINE nil or omitted means do not
 include the height of the mode- or header-line of WINDOW in the return
 value.  If it is either the symbol `mode-line' or `header-line', include
 only the height of that line, if present, in the return value.  If t,
@@ -11500,7 +11592,7 @@ prepare_menu_bars (void)
                }
            }
        }
-      safe_call1 (Vpre_redisplay_function, windows);
+      safe__call1 (true, Vpre_redisplay_function, windows);
     }
 
   /* Update all frame titles based on their buffer names, etc.  We do
@@ -14344,7 +14436,7 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
                                              pos_after, 0);
 
                if (prop_pos >= pos_before)
-                 bpos_max = prop_pos - 1;
+                 bpos_max = prop_pos;
              }
            if (INTEGERP (chprop))
              {
@@ -14418,7 +14510,7 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
                                              pos_after, 0);
 
                if (prop_pos >= pos_before)
-                 bpos_max = prop_pos - 1;
+                 bpos_max = prop_pos;
              }
            if (INTEGERP (chprop))
              {
@@ -14448,7 +14540,7 @@ set_cursor_from_row (struct window *w, struct glyph_row *row,
      GLYPH_BEFORE and GLYPH_AFTER.  */
   if (!((row->reversed_p ? glyph > glyphs_end : glyph < glyphs_end)
        && BUFFERP (glyph->object) && glyph->charpos == pt_old)
-      && !(bpos_max < pt_old && pt_old <= bpos_covered))
+      && !(bpos_max <= pt_old && pt_old <= bpos_covered))
     {
       /* An empty line has a single glyph whose OBJECT is zero and
         whose CHARPOS is the position of a newline on that line.
@@ -15680,7 +15772,51 @@ set_vertical_scroll_bar (struct window *w)
    selected_window is redisplayed.
 
    We can return without actually redisplaying the window if fonts has been
-   changed on window's frame.  In that case, redisplay_internal will retry.  */
+   changed on window's frame.  In that case, redisplay_internal will retry.
+
+   As one of the important parts of redisplaying a window, we need to
+   decide whether the previous window-start position (stored in the
+   window's w->start marker position) is still valid, and if it isn't,
+   recompute it.  Some details about that:
+
+    . The previous window-start could be in a continuation line, in
+      which case we need to recompute it when the window width
+      changes.  See compute_window_start_on_continuation_line and its
+      call below.
+
+    . The text that changed since last redisplay could include the
+      previous window-start position.  In that case, we try to salvage
+      what we can from the current glyph matrix by calling
+      try_scrolling, which see.
+
+    . Some Emacs command could force us to use a specific window-start
+      position by setting the window's force_start flag, or gently
+      propose doing that by setting the window's optional_new_start
+      flag.  In these cases, we try using the specified start point if
+      that succeeds (i.e. the window desired matrix is successfully
+      recomputed, and point location is within the window).  In case
+      of optional_new_start, we first check if the specified start
+      position is feasible, i.e. if it will allow point to be
+      displayed in the window.  If using the specified start point
+      fails, e.g., if new fonts are needed to be loaded, we abort the
+      redisplay cycle and leave it up to the next cycle to figure out
+      things.
+
+    . Note that the window's force_start flag is sometimes set by
+      redisplay itself, when it decides that the previous window start
+      point is fine and should be kept.  Search for "goto force_start"
+      below to see the details.  Like the values of window-start
+      specified outside of redisplay, these internally-deduced values
+      are tested for feasibility, and ignored if found to be
+      unfeasible.
+
+    . Note that the function try_window, used to completely redisplay
+      a window, accepts the window's start point as its argument.
+      This is used several times in the redisplay code to control
+      where the window start will be, according to user options such
+      as scroll-conservatively, and also to ensure the screen line
+      showing point will be fully (as opposed to partially) visible on
+      display.  */
 
 static void
 redisplay_window (Lisp_Object window, bool just_this_one_p)
@@ -15726,6 +15862,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   eassert (XMARKER (w->start)->buffer == buffer);
   eassert (XMARKER (w->pointm)->buffer == buffer);
 
+  /* We come here again if we need to run window-text-change-functions
+     below.  */
  restart:
   reconsider_clip_changes (w);
   frame_line_height = default_line_pixel_height (w);
@@ -15790,7 +15928,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
        && !current_buffer->prevent_redisplay_optimizations_p
        && !window_outdated (w));
 
-  /* Run the window-bottom-change-functions
+  /* Run the window-text-change-functions
      if it is possible that the text on the screen has changed
      (either due to modification of the text, or any other reason).  */
   if (!current_matrix_up_to_date_p
@@ -16391,12 +16529,50 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   /* Consider the following case: Window starts at BEGV, there is
      invisible, intangible text at BEGV, so that display starts at
      some point START > BEGV.  It can happen that we are called with
-     PT somewhere between BEGV and START.  Try to handle that case.  */
+     PT somewhere between BEGV and START.  Try to handle that case,
+     and similar ones.  */
   if (w->cursor.vpos < 0)
     {
-      struct glyph_row *row = w->current_matrix->rows;
-      if (row->mode_line_p)
-       ++row;
+      /* First, try locating the proper glyph row for PT.  */
+      struct glyph_row *row =
+       row_containing_pos (w, PT, w->current_matrix->rows, NULL, 0);
+
+      /* Sometimes point is at the beginning of invisible text that is
+        before the 1st character displayed in the row.  In that case,
+        row_containing_pos fails to find the row, because no glyphs
+        with appropriate buffer positions are present in the row.
+        Therefore, we next try to find the row which shows the 1st
+        position after the invisible text.  */
+      if (!row)
+       {
+         Lisp_Object val =
+           get_char_property_and_overlay (make_number (PT), Qinvisible,
+                                          Qnil, NULL);
+
+         if (TEXT_PROP_MEANS_INVISIBLE (val))
+           {
+             ptrdiff_t alt_pos;
+             Lisp_Object invis_end =
+               Fnext_single_char_property_change (make_number (PT), Qinvisible,
+                                                  Qnil, Qnil);
+
+             if (NATNUMP (invis_end))
+               alt_pos = XFASTINT (invis_end);
+             else
+               alt_pos = ZV;
+             row = row_containing_pos (w, alt_pos, w->current_matrix->rows,
+                                       NULL, 0);
+           }
+       }
+      /* Finally, fall back on the first row of the window after the
+        header line (if any).  This is slightly better than not
+        displaying the cursor at all.  */
+      if (!row)
+       {
+         row = w->current_matrix->rows;
+         if (row->mode_line_p)
+           ++row;
+       }
       set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0);
     }
 
@@ -18607,6 +18783,7 @@ insert_left_trunc_glyphs (struct it *it)
   truncate_it.current_x = 0;
   truncate_it.face_id = DEFAULT_FACE_ID;
   truncate_it.glyph_row = &scratch_glyph_row;
+  truncate_it.area = TEXT_AREA;
   truncate_it.glyph_row->used[TEXT_AREA] = 0;
   CHARPOS (truncate_it.position) = BYTEPOS (truncate_it.position) = -1;
   truncate_it.object = make_number (0);
@@ -20585,6 +20762,10 @@ Value is the new character position of point.  */)
       && !b->clip_changed
       && !b->prevent_redisplay_optimizations_p
       && !window_outdated (w)
+      /* We rely below on the cursor coordinates to be up to date, but
+        we cannot trust them if some command moved point since the
+        last complete redisplay.  */
+      && w->last_point == BUF_PT (b)
       && w->cursor.vpos >= 0
       && w->cursor.vpos < w->current_matrix->nrows
       && (row = MATRIX_ROW (w->current_matrix, w->cursor.vpos))->enabled_p)
@@ -20857,6 +21038,30 @@ Value is the new character position of point.  */)
              move_it_by_lines (&it, -1);
              target_x = it.last_visible_x - !FRAME_WINDOW_P (it.f);
              target_is_eol_p = true;
+             /* Under word-wrap, we don't know the x coordinate of
+                the last character displayed on the previous line,
+                which immediately precedes the wrap point.  To find
+                out its x coordinate, we try moving to the right
+                margin of the window, which will stop at the wrap
+                point, and then reset target_x to point at the
+                character that precedes the wrap point.  This is not
+                needed on GUI frames, because (see below) there we
+                move from the left margin one grapheme cluster at a
+                time, and stop when we hit the wrap point.  */
+             if (!FRAME_WINDOW_P (it.f) && it.line_wrap == WORD_WRAP)
+               {
+                 void *it_data = NULL;
+                 struct it it2;
+
+                 SAVE_IT (it2, it, it_data);
+                 move_it_in_display_line_to (&it, ZV, target_x,
+                                             MOVE_TO_POS | MOVE_TO_X);
+                 /* If we arrived at target_x, that _is_ the last
+                    character on the previous line.  */
+                 if (it.current_x != target_x)
+                   target_x = it.current_x - 1;
+                 RESTORE_IT (&it, &it2, it_data);
+               }
            }
        }
       else
@@ -21751,7 +21956,7 @@ display_mode_element (struct it *it, int depth, int field_width, int precision,
            if (CONSP (XCDR (elt)))
              {
                Lisp_Object spec;
-               spec = safe_eval (XCAR (XCDR (elt)));
+               spec = safe__eval (true, XCAR (XCDR (elt)));
                n += display_mode_element (it, depth, field_width - n,
                                           precision - n, spec, props,
                                           risky);
@@ -27220,7 +27425,7 @@ show_mouse_face (Mouse_HLInfo *hlinfo, enum draw_glyphs_face draw)
   if (/* If window is in the process of being destroyed, don't bother
         to do anything.  */
       w->current_matrix != NULL
-      /* Don't update mouse highlight if hidden */
+      /* Don't update mouse highlight if hidden */
       && (draw != DRAW_MOUSE_FACE || !hlinfo->mouse_face_hidden)
       /* Recognize when we are called to operate on rows that don't exist
         anymore.  This can happen when a window is split.  */