unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Po Lu <luangruo@yahoo.com>
To: Eli Zaretskii <eliz@gnu.org>
Cc: emacs-devel@gnu.org
Subject: Re: Allowing point to be outside the window?
Date: Sun, 06 Feb 2022 15:22:57 +0800	[thread overview]
Message-ID: <878ruoqx0u.fsf@yahoo.com> (raw)
In-Reply-To: <87fsqh9o7s.fsf@yahoo.com> (Po Lu's message of "Sat, 25 Dec 2021 14:45:59 +0800")

[-- Attachment #1: Type: text/plain, Size: 484 bytes --]

Po Lu <luangruo@yahoo.com> writes:

> To be completely clear, the "recenter around point" fallback you allude
> to is the code under the `recenter' label in `redisplay_window',
> correct?
>
> Please forgive me if this has been answered before, but I haven't been
> working on this in a while, and my memory is already getting rusty.
>
> Thanks.

How about this: we recenter around the position of the first modified
character?  Point isn't moved at all during that process.

Thanks.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: test.diff --]
[-- Type: text/x-patch, Size: 29096 bytes --]

diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi
index 4fcd2a3f7d..fabb53bb3d 100644
--- a/doc/emacs/display.texi
+++ b/doc/emacs/display.texi
@@ -250,10 +250,18 @@ Auto Scrolling
 @section Automatic Scrolling
 
 @cindex automatic scrolling
-  Emacs performs @dfn{automatic scrolling} when point moves out of the
-visible portion of the text.  Normally, automatic scrolling centers
-point vertically in the window, but there are several ways to alter
-this behavior.
+  Emacs by default performs @dfn{automatic scrolling} when point moves
+out of the visible portion of the text.  Normally, automatic scrolling
+centers point vertically in the window, but there are several ways to
+alter this behavior.
+
+@vindex keep-point-visible
+  If @code{keep-point-visible} is nil, redisplay will not move recenter
+the display when the window start is changed.
+
+@vindex scroll-move-point
+  If @code{scroll-move-point} is nil, scrolling commands will not move
+point to keep it inside the visible part of the window.
 
 @vindex scroll-conservatively
 @vindex scroll-minibuffer-conservatively
diff --git a/etc/NEWS b/etc/NEWS
index 6c5aeacb7b..a696a1dffc 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -392,6 +392,15 @@ are met.  The conditions are given by the argument, which can be
 
 * Editing Changes in Emacs 29.1
 
+** New variable 'keep-point-visible'.
+This variable controls if redisplay will try to keep point visible
+inside the window.
+
++++
+** New variable 'scroll-move-point'.
+This variable controls if scrolling moves point to stay inside the
+window.
+
 ---
 ** Indentation of 'cl-flet' and 'cl-labels' has changed.
 These forms now indent like this:
diff --git a/lisp/pixel-scroll.el b/lisp/pixel-scroll.el
index 54724ca328..8f07e58428 100644
--- a/lisp/pixel-scroll.el
+++ b/lisp/pixel-scroll.el
@@ -507,20 +507,22 @@ pixel-scroll-precision-scroll-down-page
          (edges (window-edges nil t))
          (usable-height (- (nth 3 edges)
                            (nth 1 edges)))
-         (next-pos (save-excursion
-                     (goto-char desired-start)
-                     (when (zerop (vertical-motion (1+ scroll-margin)))
-                       (set-window-start nil desired-start)
-                       (signal 'end-of-buffer nil))
-                     (while (when-let ((posn (posn-at-point)))
-                              (< (cdr (posn-x-y posn)) delta))
-                       (when (zerop (vertical-motion 1))
+         (next-pos (when keep-point-visible
+                     (save-excursion
+                       (goto-char desired-start)
+                       (when (zerop (vertical-motion (1+ scroll-margin)))
                          (set-window-start nil desired-start)
-                         (signal 'end-of-buffer nil)))
-                     (point)))
+                         (signal 'end-of-buffer nil))
+                       (while (when-let ((posn (posn-at-point)))
+                                (< (cdr (posn-x-y posn)) delta))
+                         (when (zerop (vertical-motion 1))
+                           (set-window-start nil desired-start)
+                           (signal 'end-of-buffer nil)))
+                       (point))))
          (scroll-preserve-screen-position nil)
          (auto-window-vscroll nil))
-    (when (and (or (< (point) next-pos))
+    (when (and keep-point-visible
+               (or (< (point) next-pos))
                (let ((pos-visibility (pos-visible-in-window-p next-pos nil t)))
                  (and pos-visibility
                       (or (eq (length pos-visibility) 2)
@@ -554,15 +556,17 @@ pixel-scroll-precision-scroll-up-page
          (max-y (- (nth 3 edges)
                    (nth 1 edges)))
          (usable-height max-y)
-         (posn (posn-at-x-y 0 (+ (window-tab-line-height)
-                                 (window-header-line-height)
-                                 (- max-y delta))))
-         (point (posn-point posn))
-         (up-point (save-excursion
-                     (goto-char point)
-                     (vertical-motion (- (1+ scroll-margin)))
-                     (point))))
-    (when (> (point) up-point)
+         (posn (when keep-point-visible
+                 (posn-at-x-y 0 (+ (window-tab-line-height)
+                                   (window-header-line-height)
+                                   (- max-y delta)))))
+         (point (when posn (posn-point posn)))
+         (up-point (when point
+                     (save-excursion
+                       (goto-char point)
+                       (vertical-motion (- (1+ scroll-margin)))
+                       (point)))))
+    (when (and keep-point-visible (> (point) up-point))
       (when (let ((pos-visible (pos-visible-in-window-p up-point nil t)))
               (or (eq (length pos-visible) 2)
                   (when-let* ((posn (posn-at-point up-point))
diff --git a/src/window.c b/src/window.c
index 449f2b0cc5..b2fd76652f 100644
--- a/src/window.c
+++ b/src/window.c
@@ -5578,7 +5578,8 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
      something like (scroll-down 1) with PT in the line before
      the partially visible one would recenter.  */
 
-  if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos))
+  if (!pos_visible_p (w, PT, &x, &y, &rtop, &rbot, &rowh, &vpos)
+      && scroll_move_point)
     {
       itdata = bidi_shelve_cache ();
       /* Move backward half the height of the window.  Performance note:
@@ -5659,8 +5660,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
 		  w->start_at_line_beg = true;
 		  wset_update_mode_line (w);
 		  /* Set force_start so that redisplay_window will run the
-		     window-scroll-functions.  */
-		  w->force_start = true;
+		     window-scroll-functions, unless scroll_move_point is false,
+		     in which case forcing the start will cause recentering.  */
+		  w->force_start = scroll_move_point;
 		  return;
 		}
 	    }
@@ -5844,8 +5846,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
       w->start_at_line_beg = (pos == BEGV || FETCH_BYTE (bytepos - 1) == '\n');
       wset_update_mode_line (w);
       /* Set force_start so that redisplay_window will run the
-	 window-scroll-functions.  */
-      w->force_start = true;
+	 window-scroll-functions, unless scroll_move_point is false,
+	 in which case forcing the start will cause recentering.  */
+      w->force_start = scroll_move_point;
     }
 
   /* The rest of this function uses current_y in a nonstandard way,
@@ -5857,7 +5860,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
      even if there is a header line.  */
   this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS);
 
-  if (n > 0)
+  if (scroll_move_point)
     {
       int last_y = it.last_visible_y - this_scroll_margin - 1;
 
@@ -5873,142 +5876,159 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror)
 	      || EQ (Vscroll_preserve_screen_position, Qt)))
 	/* We found PT at a legitimate height.  Leave it alone.  */
 	;
-      else
+      else if (n > 0)
 	{
-	  if (window_scroll_pixel_based_preserve_y >= 0)
-	    {
-	      /* Don't enter the scroll margin at the end of the window.  */
-	      int goal_y = min (last_y, window_scroll_pixel_based_preserve_y);
-
-	      /* If we have a header line, take account of it.  This
-		 is necessary because we set it.current_y to 0, above.  */
-	      move_it_to (&it, -1,
-			  window_scroll_pixel_based_preserve_x,
-			  goal_y - WINDOW_TAB_LINE_HEIGHT (w)
-				 - WINDOW_HEADER_LINE_HEIGHT (w),
-			  -1, MOVE_TO_Y | MOVE_TO_X);
-	    }
+	  int last_y = it.last_visible_y - this_scroll_margin - 1;
 
-	  /* Get out of the scroll margin at the top of the window.  */
-	  while (it.current_y < this_scroll_margin)
+	  /* We moved the window start towards ZV, so PT may be now
+	     in the scroll margin at the top.  */
+	  move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
+	  if (IT_CHARPOS (it) == PT
+	      && it.current_y >= this_scroll_margin
+	      && it.current_y <= last_y - WINDOW_TAB_LINE_HEIGHT (w)
+	      - WINDOW_HEADER_LINE_HEIGHT (w)
+	      && (NILP (Vscroll_preserve_screen_position)
+		  || EQ (Vscroll_preserve_screen_position, Qt)))
+	    /* We found PT at a legitimate height.  Leave it alone.  */
+	    ;
+	  else
 	    {
-	      int prev = it.current_y;
-	      move_it_by_lines (&it, 1);
-	      if (prev == it.current_y)
-		break;
+	      if (window_scroll_pixel_based_preserve_y >= 0)
+		{
+		  /* Don't enter the scroll margin at the end of the window.  */
+		  int goal_y = min (last_y, window_scroll_pixel_based_preserve_y);
+
+		  /* If we have a header line, take account of it.  This
+		     is necessary because we set it.current_y to 0, above.  */
+		  move_it_to (&it, -1,
+			      window_scroll_pixel_based_preserve_x,
+			      goal_y - WINDOW_TAB_LINE_HEIGHT (w)
+			      - WINDOW_HEADER_LINE_HEIGHT (w),
+			      -1, MOVE_TO_Y | MOVE_TO_X);
+		}
+
+	      /* Get out of the scroll margin at the top of the window.  */
+	      while (it.current_y < this_scroll_margin)
+		{
+		  int prev = it.current_y;
+		  move_it_by_lines (&it, 1);
+		  if (prev == it.current_y)
+		    break;
+		}
+	      SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+	      /* Fix up the Y position to preserve, if it is inside the
+		 scroll margin at the window top.  */
+	      if (window_scroll_pixel_based_preserve_y >= 0
+		  && window_scroll_pixel_based_preserve_y < this_scroll_margin)
+		window_scroll_pixel_based_preserve_y = this_scroll_margin;
 	    }
-	  SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
-	  /* Fix up the Y position to preserve, if it is inside the
-	     scroll margin at the window top.  */
-	  if (window_scroll_pixel_based_preserve_y >= 0
-	      && window_scroll_pixel_based_preserve_y < this_scroll_margin)
-	    window_scroll_pixel_based_preserve_y = this_scroll_margin;
 	}
-    }
-  else if (n < 0)
-    {
-      ptrdiff_t charpos, bytepos;
-      bool partial_p;
-
-      /* Save our position, for the
-	 window_scroll_pixel_based_preserve_y case.  */
-      charpos = IT_CHARPOS (it);
-      bytepos = IT_BYTEPOS (it);
-
-      /* We moved the window start towards BEGV, so PT may be now
-	 in the scroll margin at the bottom.  */
-      move_it_to (&it, PT, -1,
-		  /* We subtract WINDOW_HEADER_LINE_HEIGHT because
-		     it.y is relative to the bottom of the header
-		     line, see above.  */
-		  (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w)
-				     - WINDOW_HEADER_LINE_HEIGHT (w)
-		   - partial_line_height (&it) - this_scroll_margin - 1),
-		  -1,
-		  MOVE_TO_POS | MOVE_TO_Y);
-
-      /* Save our position, in case it's correct.  */
-      charpos = IT_CHARPOS (it);
-      bytepos = IT_BYTEPOS (it);
-
-      /* If PT is in the screen line at the last fully visible line,
-	 move_it_to will stop at X = 0 in that line, because the
-	 required Y coordinate is reached there.  See if we can get to
-	 PT without descending lower in Y, and if we can, it means we
-	 reached PT before the scroll margin.  */
-      if (charpos != PT)
+      else if (n < 0)
 	{
-	  struct it it2;
-	  void *it_data;
+	  ptrdiff_t charpos, bytepos;
+	  bool partial_p;
 
-	  it2 = it;
-	  it_data = bidi_shelve_cache ();
-	  move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
-	  if (IT_CHARPOS (it) == PT && it.current_y == it2.current_y)
+	  /* Save our position, for the
+	     window_scroll_pixel_based_preserve_y case.  */
+	  charpos = IT_CHARPOS (it);
+	  bytepos = IT_BYTEPOS (it);
+
+	  /* We moved the window start towards BEGV, so PT may be now
+	     in the scroll margin at the bottom.  */
+	  move_it_to (&it, PT, -1,
+		      /* We subtract WINDOW_HEADER_LINE_HEIGHT because
+			 it.y is relative to the bottom of the header
+			 line, see above.  */
+		      (it.last_visible_y - WINDOW_TAB_LINE_HEIGHT (w)
+		       - WINDOW_HEADER_LINE_HEIGHT (w)
+		       - partial_line_height (&it) - this_scroll_margin - 1),
+		      -1,
+		      MOVE_TO_POS | MOVE_TO_Y);
+
+	  /* Save our position, in case it's correct.  */
+	  charpos = IT_CHARPOS (it);
+	  bytepos = IT_BYTEPOS (it);
+
+	  /* If PT is in the screen line at the last fully visible line,
+	     move_it_to will stop at X = 0 in that line, because the
+	     required Y coordinate is reached there.  See if we can get to
+	     PT without descending lower in Y, and if we can, it means we
+	     reached PT before the scroll margin.  */
+	  if (charpos != PT)
 	    {
-	      charpos = IT_CHARPOS (it);
-	      bytepos = IT_BYTEPOS (it);
-	      bidi_unshelve_cache (it_data, true);
+	      struct it it2;
+	      void *it_data;
+
+	      it2 = it;
+	      it_data = bidi_shelve_cache ();
+	      move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
+	      if (IT_CHARPOS (it) == PT && it.current_y == it2.current_y)
+		{
+		  charpos = IT_CHARPOS (it);
+		  bytepos = IT_BYTEPOS (it);
+		  bidi_unshelve_cache (it_data, true);
+		}
+	      else
+		{
+		  it = it2;
+		  bidi_unshelve_cache (it_data, false);
+		}
 	    }
+
+	  /* See if point is on a partially visible line at the end.  */
+	  if (it.what == IT_EOB)
+	    partial_p =
+	      it.current_y + it.ascent + it.descent
+	      > it.last_visible_y - this_scroll_margin
+	      - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
 	  else
 	    {
-	      it = it2;
-	      bidi_unshelve_cache (it_data, false);
+	      move_it_by_lines (&it, 1);
+	      partial_p =
+		it.current_y
+		> it.last_visible_y - this_scroll_margin
+		- WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
 	    }
-	}
-
-      /* See if point is on a partially visible line at the end.  */
-      if (it.what == IT_EOB)
-	partial_p =
-	  it.current_y + it.ascent + it.descent
-	  > it.last_visible_y - this_scroll_margin
-	  - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
-      else
-	{
-	  move_it_by_lines (&it, 1);
-	  partial_p =
-	    it.current_y
-	    > it.last_visible_y - this_scroll_margin
-	      - WINDOW_TAB_LINE_HEIGHT (w) - WINDOW_HEADER_LINE_HEIGHT (w);
-	}
 
-      if (charpos == PT && !partial_p
-          && (NILP (Vscroll_preserve_screen_position)
-	      || EQ (Vscroll_preserve_screen_position, Qt)))
-	/* We found PT before we found the display margin, so PT is ok.  */
-	;
-      else if (window_scroll_pixel_based_preserve_y >= 0)
-	{
-	  int goal_y = min (it.last_visible_y - this_scroll_margin - 1,
-			    window_scroll_pixel_based_preserve_y);
-
-	  /* Don't let the preserved screen Y coordinate put us inside
-	     any of the two margins.  */
-	  if (goal_y < this_scroll_margin)
-	    goal_y = this_scroll_margin;
-	  SET_TEXT_POS_FROM_MARKER (start, w->start);
-	  start_display (&it, w, start);
-	  /* It would be wrong to subtract WINDOW_HEADER_LINE_HEIGHT
-	     here because we called start_display again and did not
-	     alter it.current_y this time.  */
-	  move_it_to (&it, -1, window_scroll_pixel_based_preserve_x,
-		      goal_y, -1, MOVE_TO_Y | MOVE_TO_X);
-	  SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
-	}
-      else
-	{
-	  if (partial_p)
-	    /* The last line was only partially visible, so back up two
-	       lines to make sure we're on a fully visible line.  */
+	  if (charpos == PT && !partial_p
+	      && (NILP (Vscroll_preserve_screen_position)
+		  || EQ (Vscroll_preserve_screen_position, Qt)))
+	    /* We found PT before we found the display margin, so PT is ok.  */
+	    ;
+	  else if (window_scroll_pixel_based_preserve_y >= 0)
 	    {
-	      move_it_by_lines (&it, -2);
+	      int goal_y = min (it.last_visible_y - this_scroll_margin - 1,
+				window_scroll_pixel_based_preserve_y);
+
+	      /* Don't let the preserved screen Y coordinate put us inside
+		 any of the two margins.  */
+	      if (goal_y < this_scroll_margin)
+		goal_y = this_scroll_margin;
+	      SET_TEXT_POS_FROM_MARKER (start, w->start);
+	      start_display (&it, w, start);
+	      /* It would be wrong to subtract WINDOW_HEADER_LINE_HEIGHT
+		 here because we called start_display again and did not
+		 alter it.current_y this time.  */
+	      move_it_to (&it, -1, window_scroll_pixel_based_preserve_x,
+			  goal_y, -1, MOVE_TO_Y | MOVE_TO_X);
 	      SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
 	    }
 	  else
-	    /* No, the position we saved is OK, so use it.  */
-	    SET_PT_BOTH (charpos, bytepos);
+	    {
+	      if (partial_p)
+		/* The last line was only partially visible, so back up two
+		   lines to make sure we're on a fully visible line.  */
+		{
+		  move_it_by_lines (&it, -2);
+		  SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
+		}
+	      else
+		/* No, the position we saved is OK, so use it.  */
+		SET_PT_BOTH (charpos, bytepos);
+	    }
 	}
     }
+
   bidi_unshelve_cache (itdata, false);
 
   if (adjust_old_pointm)
@@ -8513,6 +8533,10 @@ syms_of_window (void)
 displayed after a scrolling operation to be somewhat inaccurate.  */);
   fast_but_imprecise_scrolling = false;
 
+  DEFVAR_BOOL ("scroll-move-point", scroll_move_point,
+	       doc: /* If nil, don't move point to fit inside the window when scrolling.  */);
+  scroll_move_point = true;
+
   defsubr (&Sselected_window);
   defsubr (&Sold_selected_window);
   defsubr (&Sminibuffer_window);
diff --git a/src/xdisp.c b/src/xdisp.c
index db9bc512a9..1f882f6996 100644
--- a/src/xdisp.c
+++ b/src/xdisp.c
@@ -16224,6 +16224,8 @@ #define AINC(a,i)							\
   tlbufpos = this_line_start_pos;
   tlendpos = this_line_end_pos;
   if (!consider_all_windows_p
+      /* TODO: enable this optimization.  */
+      && keep_point_visible
       && CHARPOS (tlbufpos) > 0
       && !w->update_mode_line
       && !current_buffer->clip_changed
@@ -17874,6 +17876,9 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
   else
     scroll_max = 0;
 
+  if (!keep_point_visible && PT == w->last_point)
+    goto out;
+
  too_near_end:
 
   /* Decide whether to scroll down.  */
@@ -18110,6 +18115,8 @@ try_scrolling (Lisp_Object window, bool just_this_one_p,
   /* Run window scroll functions.  */
   startp = run_window_scroll_functions (window, startp);
 
+ out:
+
   /* Display the window.  Give up if new fonts are loaded, or if point
      doesn't appear.  */
   if (!try_window (window, startp, 0))
@@ -18844,6 +18851,9 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   int frame_line_height, margin;
   bool use_desired_matrix;
   void *itdata = NULL;
+  bool need_recenter_even_if_point_can_be_invisible = false;
+  ptrdiff_t that_recentering_position;
+  ptrdiff_t that_recentering_byte;
 
   SET_TEXT_POS (lpoint, PT, PT_BYTE);
   opoint = lpoint;
@@ -19022,6 +19032,36 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 
   SET_TEXT_POS_FROM_MARKER (startp, w->start);
 
+  if (!keep_point_visible && window_outdated (w))
+    {
+      /* If some text changed between window start, then recenter the
+	 display around the first character that changed, to avoid
+	 confusing the user by not updating the display to reflect the
+	 changes.  */
+      ptrdiff_t last_changed_charpos, first_changed_charpos;
+
+      /* 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
+	 set end_unchanged to 0 in that case.  */
+      if (GPT - BEG < BEG_UNCHANGED)
+	BEG_UNCHANGED = GPT - BEG;
+      if (Z - GPT < END_UNCHANGED)
+	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 (last_changed_charpos < CHARPOS (startp))
+	{
+	  that_recentering_position = first_changed_charpos;
+	  that_recentering_byte = buf_charpos_to_bytepos (current_buffer,
+							  first_changed_charpos);
+	  need_recenter_even_if_point_can_be_invisible = true;
+	}
+    }
+
   /* If someone specified a new starting point but did not insist,
      check whether it can be used.  */
   if ((w->optional_new_start || window_frozen_p (w))
@@ -19146,6 +19186,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	    new_vpos = MATRIX_ROW_BOTTOM_Y (r);
 	  else	/* Give up and just move to the middle of the window.  */
 	    new_vpos = window_box_height (w) / 2;
+
 	}
 
       if (!cursor_row_fully_visible_p (w, false, false, false))
@@ -19393,10 +19434,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	  IF_DEBUG (debug_method_add (w, "1"));
 	  clear_glyph_matrix (w->desired_matrix);
 	  if (try_window (window, startp, TRY_WINDOW_CHECK_MARGINS) < 0)
-	    /* -1 means we need to scroll.
-	       0 means we need new matrices, but fonts_changed
-	       is set in that case, so we will detect it below.  */
-	    goto try_to_scroll;
+	    {
+	      /* -1 means we need to scroll.
+		 0 means we need new matrices, but fonts_changed
+		 is set in that case, so we will detect it below.  */
+	      goto try_to_scroll;
+	    }
 	}
 
       if (f->fonts_changed)
@@ -19425,7 +19468,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 
  try_to_scroll:
 
-  /* Redisplay the mode line.  Select the buffer properly for that.  */
+ /* Redisplay the mode line.  Select the buffer properly for that.  */
   if (!update_mode_line)
     {
       update_mode_line = true;
@@ -19471,6 +19514,13 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	}
     }
 
+  if (!keep_point_visible
+      && !need_recenter_even_if_point_can_be_invisible)
+    {
+      if (PT == w->last_point)
+	goto maybe_try_window;
+    }
+
   /* Finally, just choose a place to start which positions point
      according to user preferences.  */
 
@@ -19484,9 +19534,16 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   if (!buffer_unchanged_p)
     w->base_line_number = 0;
 
-  /* Determine the window start relative to point.  */
-  init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
+  /* Determine the window start relative to where we want to recenter
+     to.  */
+
+  if (need_recenter_even_if_point_can_be_invisible)
+    init_iterator (&it, w, that_recentering_position,
+		   that_recentering_byte, NULL, DEFAULT_FACE_ID);
+  else
+    init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID);
   it.current_y = it.last_visible_y;
+
   if (centering_position < 0)
     {
       ptrdiff_t margin_pos = CHARPOS (startp);
@@ -19513,7 +19570,9 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
 	  margin_pos = IT_CHARPOS (it1);
 	  RESTORE_IT (&it, &it, it1data);
 	}
-      scrolling_up = PT > margin_pos;
+      scrolling_up = (need_recenter_even_if_point_can_be_invisible
+		      ? that_recentering_position
+		      : PT) > margin_pos;
       aggressive =
 	scrolling_up
 	? BVAR (current_buffer, scroll_up_aggressively)
@@ -19754,6 +19813,53 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
       centering_position = 0;
       goto recenter;
     }
+  goto done;
+
+ maybe_try_window:
+
+  /* Set the window start position here explicitly if it is outside
+     the accessible portion of the buffer.  */
+
+  if (CHARPOS (startp) < BEGV
+      || CHARPOS (startp) > ZV)
+    {
+      if (CHARPOS (startp) < BEGV)
+	set_marker_both (w->start, Qnil, BEGV, BEGV_BYTE);
+      else
+	set_marker_both (w->start, Qnil, ZV, ZV_BYTE);
+
+      SET_TEXT_POS_FROM_MARKER (startp, w->start);
+
+      /* Run scroll hooks.  */
+      startp = run_window_scroll_functions (window, startp);
+    }
+
+  /* We invoke try_window and try_window_reusing_current_matrix below,
+     and they manipulate the bidi cache.  Save and restore the cache
+     state of our iterator, so we could continue using it after that.  */
+  itdata = bidi_shelve_cache ();
+
+  /* Redisplay the window.  */
+  use_desired_matrix = false;
+  if (!current_matrix_up_to_date_p
+      || windows_or_buffers_changed
+      || f->cursor_type_changed
+      /* Don't use try_window_reusing_current_matrix in this case
+	 because it can have changed the buffer.  */
+      || !NILP (Vwindow_scroll_functions)
+      || !just_this_one_p
+      || MINI_WINDOW_P (w)
+      || !(used_current_matrix_p
+	   = try_window_reusing_current_matrix (w)))
+    use_desired_matrix = (try_window (window, startp, 0) == 1);
+
+  bidi_unshelve_cache (itdata, false);
+
+  /* If new fonts have been loaded (due to fontsets), give up.  We
+     have to start a new redisplay since we need to re-adjust glyph
+     matrices.  */
+  if (f->fonts_changed)
+    goto need_larger_matrices;
 
  done:
 
@@ -19761,6 +19867,11 @@ redisplay_window (Lisp_Object window, bool just_this_one_p)
   w->start_at_line_beg = (CHARPOS (startp) == BEGV
 			  || FETCH_BYTE (BYTEPOS (startp) - 1) == '\n');
 
+  if (!keep_point_visible
+      && (w->cursor.vpos == -1)
+      && w->phys_cursor_on_p)
+    erase_phys_cursor (w);
+
   /* Display the mode line, header line, and tab-line, if we must.  */
   if ((update_mode_line
        /* If window not full width, must redo its mode line
@@ -28638,6 +28749,7 @@ fill_composite_glyph_string (struct glyph_string *s, struct face *base_face,
 
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28690,6 +28802,7 @@ fill_gstring_glyph_string (struct glyph_string *s, int face_id,
   s->cmp_to = glyph->slice.cmp.to + 1;
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28759,6 +28872,7 @@ fill_glyphless_glyph_string (struct glyph_string *s, int face_id,
   s->font = s->face->font ? s->face->font : FRAME_FONT (s->f);
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28834,6 +28948,7 @@ fill_glyph_string (struct glyph_string *s, int face_id,
 
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28879,6 +28994,7 @@ fill_image_glyph_string (struct glyph_string *s)
   s->font = s->face->font;
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28905,6 +29021,7 @@ fill_xwidget_glyph_string (struct glyph_string *s)
   s->font = s->face->font;
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -28942,6 +29059,7 @@ fill_stretch_glyph_string (struct glyph_string *s, int start, int end)
   s->font = s->face->font;
   if (s->hl == DRAW_MOUSE_FACE
       || (s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w)))
@@ -29231,6 +29349,7 @@ set_glyph_string_background_width (struct glyph_string *s, int start, int last_x
 #ifdef HAVE_WINDOW_SYSTEM
       if (FRAME_WINDOW_P (s->f)
 	  && s->hl == DRAW_CURSOR
+	  && s->w->phys_cursor.vpos != -1
 	  && MATRIX_ROW (s->w->current_matrix,
 			 s->w->phys_cursor.vpos)->mouse_face_p
 	  && cursor_in_mouse_face_p (s->w))
@@ -32608,6 +32727,9 @@ display_and_set_cursor (struct window *w, bool on,
 	      && new_cursor_width != w->phys_cursor_width)))
     erase_phys_cursor (w);
 
+  if (w->cursor.vpos == -1)
+    return;
+
   /* Don't check phys_cursor_on_p here because that flag is only set
      to false in some cases where we know that the cursor has been
      completely erased, to avoid the extra work of erasing the cursor
@@ -32968,6 +33090,9 @@ cursor_in_mouse_face_p (struct window *w)
   int vpos = w->phys_cursor.vpos;
   struct glyph_row *row = MATRIX_ROW (w->current_matrix, vpos);
 
+  if (vpos == -1)
+    return false;
+
   /* When the window is hscrolled, cursor hpos can legitimately be out
      of bounds, but we draw the cursor at the corresponding window
      margin in that case.  */
@@ -35717,6 +35842,10 @@ syms_of_xdisp (void)
   x_stretch_cursor_p = 0;
 #endif
 
+  DEFVAR_BOOL ("keep-point-visible", keep_point_visible,
+	       doc: /* Non-nil means to keep the point visible.  */);
+  keep_point_visible = 1;
+
   DEFVAR_LISP ("show-trailing-whitespace", Vshow_trailing_whitespace,
     doc: /* Non-nil means highlight trailing whitespace.
 The face used for trailing whitespace is `trailing-whitespace'.  */);

  parent reply	other threads:[~2022-02-06  7:22 UTC|newest]

Thread overview: 111+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <87ilwd7zaq.fsf.ref@yahoo.com>
2021-11-28  3:07 ` Allowing point to be outside the window? Po Lu
2021-11-28  8:03   ` Eli Zaretskii
2021-11-28  8:13     ` Po Lu
2021-11-28  8:41       ` Eli Zaretskii
2021-11-28 12:47         ` Po Lu
2021-11-28 12:58           ` Eli Zaretskii
2021-11-28 13:10             ` Po Lu
2021-11-28 13:44               ` Eli Zaretskii
2021-11-29  1:47                 ` Po Lu
2021-11-29 13:00                   ` Eli Zaretskii
2021-11-29 13:22                     ` Po Lu
2021-11-29 13:43                       ` Eli Zaretskii
2021-11-30  1:40                         ` Po Lu
2021-11-30 16:49                           ` [External] : " Drew Adams
2021-11-30 17:26                             ` Eli Zaretskii
2021-11-30 18:10                               ` Lars Ingebrigtsen
2021-11-30 18:32                                 ` Eli Zaretskii
2021-11-30 18:49                                 ` Stefan Kangas
2021-11-30 19:21                                   ` Eli Zaretskii
2021-11-30 20:57                                     ` Drew Adams
2021-11-30 23:41                                 ` Daniel Martín
2021-12-01  8:30                                   ` martin rudalics
2021-12-01  9:10                                     ` Juri Linkov
2021-11-30 23:20                               ` Stefan Monnier
2021-12-04 11:18                           ` Po Lu
2021-12-04 12:55                             ` Eli Zaretskii
2021-12-04 13:13                               ` Po Lu
2021-12-04 16:24                                 ` Eli Zaretskii
2021-12-05  0:40                                   ` Po Lu
2021-12-04 17:15                                 ` Eli Zaretskii
2021-12-05  0:45                                   ` Po Lu
2021-12-05  9:03                                     ` Eli Zaretskii
2021-12-06  2:11                                       ` Po Lu
2021-12-06 14:13                                         ` Eli Zaretskii
2021-12-07  2:18                                           ` Po Lu
2021-12-07 13:42                                             ` Eli Zaretskii
2021-12-08  1:17                                               ` Po Lu
2021-12-08 17:14                                                 ` Eli Zaretskii
2021-12-09  0:23                                                   ` Po Lu
2021-12-09  8:02                                                     ` Eli Zaretskii
2021-12-09  9:22                                                       ` Po Lu
2021-12-09 10:02                                                         ` Eli Zaretskii
2021-12-25  6:45                                                       ` Po Lu
2021-12-25  7:07                                                         ` Eli Zaretskii
2022-02-06  7:22                                                         ` Po Lu [this message]
2022-02-06 11:34                                                           ` Eli Zaretskii
2022-02-06 11:46                                                             ` Po Lu
2022-02-06 11:55                                                               ` Eli Zaretskii
2022-02-06 12:21                                                                 ` Po Lu
2022-02-06 16:15                                                                   ` Eli Zaretskii
2022-02-07  1:21                                                                     ` Po Lu
2022-02-07  7:21                                                                       ` Po Lu
2022-02-07 13:41                                                                         ` Eli Zaretskii
2022-02-07 13:57                                                                           ` Po Lu
2022-02-07 14:24                                                                             ` Eli Zaretskii
2022-02-08  0:58                                                                               ` Po Lu
2022-02-08 17:08                                                                                 ` Eli Zaretskii
2022-02-09  1:57                                                                                   ` Po Lu
2022-02-10 13:04                                                                                     ` Eli Zaretskii
2022-02-10 13:09                                                                                       ` Po Lu
2021-12-09 11:45                                         ` Eli Zaretskii
2021-12-09 12:19                                           ` Po Lu
2021-12-09 12:45                                             ` Eli Zaretskii
2021-12-04 13:00                             ` dick
2021-12-04 13:14                               ` tomas
2021-12-04 13:19                               ` Po Lu
2021-12-04 13:41                                 ` Eli Zaretskii
2021-12-05  0:46                                   ` Po Lu
2021-12-05  7:12                                     ` Eli Zaretskii
2021-12-05  7:16                                       ` Po Lu
2021-12-05  8:48                                         ` Eli Zaretskii
2021-12-05  9:15                                           ` Po Lu
2021-12-05  9:25                                             ` Eli Zaretskii
2021-12-05  9:31                                               ` Po Lu
2021-12-05 10:34                                                 ` Eli Zaretskii
2021-12-05 10:37                                                   ` Po Lu
2021-12-04 14:17                                 ` dick
2021-12-04 16:33                                   ` Eli Zaretskii
2021-12-04 17:13                                     ` dick
2021-12-05  0:48                                       ` Po Lu
2021-11-28 14:03       ` Alan Mackenzie
2021-11-28 14:28         ` Eric S Fraga
2021-11-28 14:39           ` Eli Zaretskii
2021-11-28 16:55             ` Eric S Fraga
2021-11-28 14:42         ` dick
2021-11-28 15:39         ` Kévin Le Gouguec
2021-11-28 15:45           ` Eli Zaretskii
2021-11-28 17:14             ` Kévin Le Gouguec
2021-11-28 16:59           ` Eric S Fraga
2021-11-28 17:30             ` Kévin Le Gouguec
2021-11-29  0:34             ` Dmitry Gutov
2021-11-29  0:34         ` Po Lu
2021-12-08  1:45           ` John Ankarström
2021-12-08 12:45             ` Eli Zaretskii
2021-12-08 13:33               ` John Ankarström
2021-12-08 13:38                 ` Po Lu
2021-12-08 13:52                   ` John Ankarström
2021-12-08 14:26                 ` Eli Zaretskii
2021-12-08 16:57                   ` Stefan Monnier
2021-12-08 19:29                     ` Yuri Khan
2021-12-09  0:16                     ` Po Lu
2021-12-08 19:21                 ` Rudolf Schlatte
2021-12-08 19:56                   ` Juri Linkov
2021-12-08 20:05                     ` André A. Gomes
2021-12-08 20:31                     ` Linux console scrollback [ Was: Allowing point to be outside the window? ] Alan Mackenzie
2021-12-09  0:17                   ` Allowing point to be outside the window? Po Lu
2021-12-08 22:25                 ` Kévin Le Gouguec
2021-12-08 23:17                   ` John Ankarström
     [not found] <9603C99D-97E7-4285-A1C1-022191B6F5CC@univie.ac.at>
2021-12-08 18:43 ` Konrad Podczeck
2021-12-08 20:47   ` John Ankarström
2021-12-09 15:34 Konrad Podczeck

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=878ruoqx0u.fsf@yahoo.com \
    --to=luangruo@yahoo.com \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).