From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Po Lu Newsgroups: gmane.emacs.devel Subject: Re: Allowing point to be outside the window? Date: Sun, 06 Feb 2022 15:22:57 +0800 Message-ID: <878ruoqx0u.fsf@yahoo.com> References: <87ilwd7zaq.fsf.ref@yahoo.com> <87ilwb68ck.fsf@yahoo.com> <83zgpnunfo.fsf@gnu.org> <87fsrf3xmd.fsf@yahoo.com> <83y257ulfp.fsf@gnu.org> <8735ne4e0e.fsf@yahoo.com> <87czmcvcs1.fsf@yahoo.com> <83sfv85y36.fsf@gnu.org> <87v904tsvv.fsf@yahoo.com> <83h7bo5m1x.fsf@gnu.org> <87ilw3ubfp.fsf@yahoo.com> <83h7bn4e55.fsf@gnu.org> <877dcipjmk.fsf@yahoo.com> <83mtld254e.fsf@gnu.org> <87lf0xjgxu.fsf@yahoo.com> <83ilw0zg38.fsf@gnu.org> <87mtlbgajq.fsf@yahoo.com> <83czm7vx0s.fsf@gnu.org> <87mtlad3sv.fsf@yahoo.com> <83mtlaurxj.fsf@gnu.org> <87fsqh9o7s.fsf@yahoo.com> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="15445"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.60 (gnu/linux) Cc: emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Sun Feb 06 08:32:28 2022 Return-path: Envelope-to: ged-emacs-devel@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1nGc27-0003l0-Fy for ged-emacs-devel@m.gmane-mx.org; Sun, 06 Feb 2022 08:32:27 +0100 Original-Received: from localhost ([::1]:33212 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1nGc25-0004n2-J0 for ged-emacs-devel@m.gmane-mx.org; Sun, 06 Feb 2022 02:32:25 -0500 Original-Received: from eggs.gnu.org ([209.51.188.92]:57634) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1nGbtB-00035M-NR for emacs-devel@gnu.org; Sun, 06 Feb 2022 02:23:14 -0500 Original-Received: from sonic302-20.consmr.mail.ne1.yahoo.com ([66.163.186.146]:40233) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1nGbt7-0003SG-VK for emacs-devel@gnu.org; Sun, 06 Feb 2022 02:23:13 -0500 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1644132186; bh=piQyciZtI7Fe6HsIkT6Ss4Vk4oh0x6Lw+WW7Wac679s=; h=From:To:Cc:Subject:References:Date:In-Reply-To:From:Subject:Reply-To; b=nFLDVM5QRjYvH9xrBXqUrJglS7uTpHBn+q9i6YT+w2IpbjvuxlYFoQn7EsZCSiefUHGj6MLzDonfqSEPvXZbXBAjWXiMQ39Ubk5AdDcBfKuyY3s2unX6aCeFOo2XJUZWD7pgSksYuz8Tz60YNN7S4mIXLlaysUwJ07zeinCs+nFvuyw4yv69MJdjPIo/B+u3YehMspNsTRLiovxwPIA1t+/m+kJ5GI840+lMCxEWgtLq5fEupoEYviozOsFwhFGkUeTYGksw0F7nsAJ+Lu73cct9fQ2Gq+BhtR6qel0t5c/9ONAxS8cfsE40tp5n897twij4hXVz+r929CIzbm4IKA== X-SONIC-DKIM-SIGN: v=1; a=rsa-sha256; c=relaxed/relaxed; d=yahoo.com; s=s2048; t=1644132186; bh=k5A83LaZHpqqejcgYdxuGYIfGRxy7hpyqCDbBfNDv+J=; h=X-Sonic-MF:From:To:Subject:Date:From:Subject; b=eQqtoC4wQUSbVRgqzTiPY7JHdiHF9NhfYuLtcVWEX3X551UWS1BFT7rzGI+qw47Xz/aJDxt9i+nQxjawid8sRDE8qygPFE0KW76MjPk2gtD5MygvvmGFJDxgCeXS8jCn4YeAdIgcjM5vjOrWrf0jcXVTHKPwtBNHuqaRIRzGGQb6+IL+IbqgUdiOJxL8X5aKJRaCKfoDia/RfbQQ6CqUXv2qKbms+4HHWCPZkYH5+u5FmetG0hDCl6QrA8eFh3vouRCSkQAgaXWttSxFjnL61mBqcuQ7GhuKFjuaBoEJzt9+W89Q+VpXvPZ4tJsvn/WzgOQ5woMIjlB60z0r9SexjA== X-YMail-OSG: ufak3t8VM1l9WwClXOUG673OCnLsf_ZvLIgti0xPnVulXDP.tZ0ko84kqPjO5WZ Z_gGK6IhBo96u_spbFvgavwuQC5It.WYILJoAGV8nRGH2eFuvo9kS2mbpNj5CbgwTBjCV8Azvi2r L67HQNwmI7E0FD1Kt8aRcg2JJheLHoVFmDTUL3Liyg5_6JGQHrFegAIAAUL8m.LcZbW6hCnRbQzL DQIH6qBMAfNu8OzNhlELFAAd3f3cW_GuZDjP6Zjsb8dcNtxCys0gq1V79YXlVTvp_aA2X7jTkdNa Hi_7UA4veweYSvHbnwZzlRVWhueKoASAQMqQLRzTZvJcP3XN416sB6hMcAwETEJY047ijNNHC2Je Ob4jt5dhkjUlcBgk_Z9dYni9DLD4bMP.I51Fjs9UitRe2Nw7NGgfuXKLdINqZ1gME.Jfs0zfUSKT Rf9OetNxXCYzRUgwvUcbQykLtanvUi.GZ3rrQSd1g6q9mR6t8eMC_RoOuXdhIAevqLd4Tvzne6iV GPHPQFU_ZzBGYq7PwwbhER11394GGGHJGK36k1hbEBHyD3Wfzb4FrN_MBhkgHRh0_7KZXb9IGa6F Y7fKkm9M7xxOU5uqUiG5rhdSoteAwypUgoXu8fquCqm.yzuX4BVoaHGh7CVASkZ5ZFqnEsBcM5Kg c2JOppFVRdsYgm3OTtRE8l1f21naP_EPGu3_mOpo6gvjCT8ddjWzmKuZh2C0EVTdkgc.Aeuv6wuY r.UQRnzGeHYyH9gWbe0TMtAV2tKaKAUhsaAaD12ji9n4ycc0SXFyxgiVIxQcf5yZPEcC.heUPNDb Vd37exmmlICrJatVRQGZq6EVRBcUf9dJMj6rIldtbg X-Sonic-MF: Original-Received: from sonic.gate.mail.ne1.yahoo.com by sonic302.consmr.mail.ne1.yahoo.com with HTTP; Sun, 6 Feb 2022 07:23:06 +0000 Original-Received: by kubenode513.mail-prod1.omega.sg3.yahoo.com (VZM Hermes SMTP Server) with ESMTPA ID 3aeac70dac5cec5dff05aa396d972ff7; Sun, 06 Feb 2022 07:23:02 +0000 (UTC) In-Reply-To: <87fsqh9o7s.fsf@yahoo.com> (Po Lu's message of "Sat, 25 Dec 2021 14:45:59 +0800") X-Mailer: WebService/1.1.19711 mail.backend.jedi.jws.acl:role.jedi.acl.token.atz.jws.hermes.yahoo Received-SPF: pass client-ip=66.163.186.146; envelope-from=luangruo@yahoo.com; helo=sonic302-20.consmr.mail.ne1.yahoo.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001, T_SCC_BODY_TEXT_LINE=-0.01 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane-mx.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.io gmane.emacs.devel:285936 Archived-At: --=-=-= Content-Type: text/plain Po Lu 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. --=-=-= Content-Type: text/x-patch Content-Disposition: attachment; filename=test.diff 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'. */); --=-=-=--