From 7b9f6ee5e0a2a5470d62b297773807844378688e Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Wed, 9 Dec 2020 18:13:35 +0200 Subject: [PATCH] Improve predictability of 'scroll-preserve-screen-position' * src/window.c (window_scroll_pixel_based): Compute the new window-start more precisely when 'scroll-preserve-screen-position' is non-nil. (Bug#8355) --- src/window.c | 48 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/src/window.c b/src/window.c index 6cd3122b43b..5df3e37fbe2 100644 --- a/src/window.c +++ b/src/window.c @@ -5669,7 +5669,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) if (whole) { ptrdiff_t start_pos = IT_CHARPOS (it); - int dy = frame_line_height; + int flh = frame_line_height; int ht = window_box_height (w); int nscls = sanitize_next_screen_context_lines (); /* In the below we divide the window box height by the frame's @@ -5677,14 +5677,37 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) box is not an integral multiple of the line height. This is important to ensure we get back to the same position when scrolling up, then down. */ - dy = n * max (dy, (ht / dy - nscls) * dy); + int dy = n * max (flh, (ht / flh - nscls) * flh); + int goal_y; + void *it_data; /* Note that move_it_vertically always moves the iterator to the start of a line. So, if the last line doesn't have a newline, we would end up at the start of the line ending at ZV. */ if (dy <= 0) { + goal_y = it.current_y - dy; move_it_vertically_backward (&it, -dy); + /* Extra precision for people who want us to preserve the + screen position of the cursor: effectively round DY to the + nearest screen line, instead of rounding to zero; the latter + causes point to move by one line after C-v followed by M-v, + if the buffer has lines of different height. */ + if (!NILP (Vscroll_preserve_screen_position) + && it.current_y - goal_y > 0.5 * flh) + { + it_data = bidi_shelve_cache (); + struct it it2 = it; + + move_it_by_lines (&it, -1); + if (it.current_y < goal_y - 0.5 * flh) + { + it = it2; + bidi_unshelve_cache (it_data, false); + } + else + bidi_unshelve_cache (it_data, true); + } /* Ensure we actually do move, e.g. in case we are currently looking at an image that is taller that the window height. */ while (start_pos == IT_CHARPOS (it) @@ -5693,8 +5716,25 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) } else if (dy > 0) { - move_it_to (&it, ZV, -1, it.current_y + dy, -1, - MOVE_TO_POS | MOVE_TO_Y); + goal_y = it.current_y + dy; + move_it_to (&it, ZV, -1, goal_y, -1, MOVE_TO_POS | MOVE_TO_Y); + /* See the comment above, for the reasons of this + extra-precision. */ + if (!NILP (Vscroll_preserve_screen_position) + && goal_y - it.current_y > 0.5 * flh) + { + it_data = bidi_shelve_cache (); + struct it it2 = it; + + move_it_by_lines (&it, 1); + if (it.current_y > goal_y + 0.5 * flh) + { + it = it2; + bidi_unshelve_cache (it_data, false); + } + else + bidi_unshelve_cache (it_data, true); + } /* Ensure we actually do move, e.g. in case we are currently looking at an image that is taller that the window height. */ while (start_pos == IT_CHARPOS (it) -- 2.39.2