Package: emacs;
Reported by: Markus Triska <triska <at> metalevel.at>
Date: Mon, 10 Feb 2025 21:57:01 UTC
Severity: normal
Found in version 31.0.50
View this message in rfc822 format
From: Stephen Berman <stephen.berman <at> gmx.net> To: Eli Zaretskii <eliz <at> gnu.org> Cc: 76186 <at> debbugs.gnu.org, triska <at> metalevel.at Subject: bug#76186: 31.0.50; (recenter 0) sometimes does not recenter as expected Date: Sat, 22 Feb 2025 22:06:18 +0100
On Sat, 22 Feb 2025 16:05:19 +0200 Eli Zaretskii <eliz <at> gnu.org> wrote: >> From: Stephen Berman <stephen.berman <at> gmx.net> >> Cc: triska <at> metalevel.at, 76186 <at> debbugs.gnu.org >> Date: Sat, 22 Feb 2025 14:46:34 +0100 >> >> On Sat, 22 Feb 2025 15:32:28 +0200 Eli Zaretskii <eliz <at> gnu.org> wrote: [...] >> > Some parts of what you describe seem to indicate that the problem is >> > not with 'recenter' itself, but with the following redisplay cycle, >> > which, for some reason, considers the window-start position >> > inappropriate and recenters point on display. But I have no idea why, >> > and it is impossible to debug this when the problem happens as part of >> > 500 iterations. >> >> I agree, and the apparent ineffectiveness of Edebug in this case is also >> frustrating. I could try using gdb with the test configuration where I >> consistly get the "unexpected recentering" after one iteration; can you >> suggest where to set the break point and any other input that may be >> helpful? > > 'recenter' takes effect in two parts: first Emacs determines where to > put window-start, and then redisplay redraws the window according to > that. > > The first part is in window.c, in Frecenter, where we have this: > > /* Set the new window start. */ > set_marker_both (w->start, w->contents, charpos, bytepos); > > So first we should determine whether this code determines the > window-start point as expected. If not, the reason is inside the > implementation of 'recenter'. > > The second part is in xdisp.c:redisplay_window, where it attempts to > obey the optional_new_start flag of the window. It starts here: > > /* 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)) > && CHARPOS (startp) >= BEGV > && CHARPOS (startp) <= ZV) > { > ptrdiff_t it_charpos; > > w->optional_new_start = false; > > This code should set the w->force_start flag. Does it? If not, what > prevents that? > > Next, we have this: > > force_start: > > /* Handle case where place to start displaying has been specified, > unless the specified location is outside the accessible range. */ > if (w->force_start) > { > > The code after this tries to use the specified window-start point, and > the question is: why it eventually rejects it (as what you see seems > to suggest)? > > HTH > > P.S. It could be that the problem is entirely elsewhere, so don't be > surprised if you step through all of that code and find that Emacs > does indeed succeed to use the window-start point the scenario expects > it to use. Thanks for the guidance. Here is the gdb session transcript produced for the test configuration where the first and third invocations of `redisplay' are commented out and only the second invocation is executed, with the result that, in all tests I've made with this configuration, the loop stops immediately after one iteration (one test series) with the message "window-start=671, point-max=793 in run 1" and the same display as after the loop stops when moving the mouse over the frame, i.e. a case of "unexpected recentering": steve [ ~/build/emacs-master/src ]$ gdb ./emacs GNU gdb (GDB) 15.2 Copyright (C) 2024 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software: you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law. Type "show copying" and "show warranty" for details. This GDB was configured as "x86_64-pc-linux-gnu". Type "show configuration" for configuration details. For bug reporting instructions, please see: <https://www.gnu.org/software/gdb/bugs/>. Find the GDB manual and other documentation resources online at: <http://www.gnu.org/software/gdb/documentation/>. For help, type "help". Type "apropos word" to search for commands related to "word"... Reading symbols from ./emacs... SIGINT is used by the debugger. Are you sure you want to change it? (y or n) [answered Y; input not from terminal] DISPLAY = :0.0 TERM = dumb Breakpoint 1 at 0x14f8d1: file /home/steve/src/emacs/emacs-master/src/emacs.c, line 424. Breakpoint 2 at 0x113d4e: file /home/steve/src/emacs/emacs-master/src/xterm.c, line 27089. (gdb) break window.c:7172 Breakpoint 3 at 0xbc850: file /home/steve/src/emacs/emacs-master/src/window.c, line 7172. (gdb) break xdisp.c:20269 Breakpoint 4 at 0x5555555f90c6: file /home/steve/src/emacs/emacs-master/src/xdisp.c, line 20269. (gdb) run -Q -l ~/comp/Emacs/bug#76186.el Starting program: /home/steve/build/emacs-master/src/emacs -Q -l ~/comp/Emacs/bug#76186.el [Thread debugging using libthread_db enabled] Using host libthread_db library "/usr/lib/libthread_db.so.1". [New Thread 0x7fffdffd86c0 (LWP 16527)] [New Thread 0x7fffdf6486c0 (LWP 16528)] [New Thread 0x7fffdecb86c0 (LWP 16529)] [New Thread 0x7fffde1706c0 (LWP 16530)] [New Thread 0x7fffdd7366c0 (LWP 16531)] Thread 1 "emacs" hit Breakpoint 3, Frecenter (arg=<optimized out>, redisplay=<optimized out>) at /home/steve/src/emacs/emacs-master/src/window.c:7172 7172 set_marker_both (w->start, w->contents, charpos, bytepos); (gdb) n 7178 w->vscroll = 0; (gdb) p w->start $2 = XIL(0x555555d2419d) (gdb) pr $2 [Thread 0x7fffde1706c0 (LWP 16530) exited] #<marker at 793 in sample> (gdb) c Continuing. Thread 1 "emacs" hit Breakpoint 4, redisplay_window (window=XIL(0x555555cdd175), just_this_one_p=just_this_one_p <at> entry=false) at /home/steve/src/emacs/emacs-master/src/xdisp.c:20269 20269 if ((w->optional_new_start || window_frozen_p (w)) (gdb) n 20275 w->optional_new_start = false; (gdb) p w->force_start $3 = false (gdb) n 20276 if (!w->force_start) (gdb) 20278 start_display (&it, w, startp); (gdb) 20279 move_it_to (&it, PT, 0, it.last_visible_y, -1, (gdb) 20283 it_charpos = IT_CHARPOS (it); (gdb) 20289 if (it.current_y == 0 || line_bottom_y (&it) < it.last_visible_y) (gdb) 20291 if (it_charpos == PT) (gdb) 20292 w->force_start = true; (gdb) p w->force_start $4 = false (gdb) n 20309 if (!BASE_LINE_NUMBER_VALID_P (w)) (gdb) p w->force_start $5 = true (gdb) n 20317 if (w->force_start) (gdb) 20322 w->force_start = false; (gdb) 20327 if (!w->preserve_vscroll_p && !window_frozen_p (w)) (gdb) 20328 w->vscroll = 0; (gdb) 20330 w->preserve_vscroll_p = false; (gdb) 20331 w->window_end_valid = false; (gdb) 20340 if (!update_mode_line (gdb) 20348 if (CHARPOS (startp) < BEGV) (gdb) 20350 else if (CHARPOS (startp) > ZV) (gdb) p ZV $6 = 793 (gdb) n 20355 if (!window_start_acceptable_p (window, CHARPOS (startp))) (gdb) p CHARPOS (startp) $7 = <optimized out> (gdb) n 20363 clear_glyph_matrix (w->desired_matrix); (gdb) 20364 if (!try_window (window, startp, 0)) (gdb) 20371 if (w->cursor.vpos < 0) (gdb) 20403 if (!cursor_row_fully_visible_p (w, false, false, false)) (gdb) 20421 else if (w->cursor.vpos >= 0) (gdb) 20426 int pixel_margin = margin * frame_line_height; (gdb) p w->cursor.vpos $8 = 0 (gdb) n 20427 bool tab_line = window_wants_tab_line (w); (gdb) 20428 bool header_line = window_wants_header_line (w); (gdb) 20434 if (w->cursor.vpos < margin + tab_line + header_line) (gdb) 20442 int window_height = window_box_height (w); (gdb) 20444 if (tab_line) (gdb) 20446 if (header_line) (gdb) 20448 if (w->cursor.y >= window_height - pixel_margin) (gdb) 20459 if (new_vpos >= 0) (gdb) 20502 if (w->cursor.vpos < 0 (gdb) 21017 SET_TEXT_POS_FROM_MARKER (startp, w->start); (gdb) p startp $9 = <optimized out> (gdb) p w->start $10 = XIL(0x555555d2419d) (gdb) pr $10 #<marker at 793 in sample> (gdb) n 21018 w->start_at_line_beg = (CHARPOS (startp) == BEGV (gdb) 21022 if ((update_mode_line (gdb) p w->start_at_line_beg $11 = true (gdb) n 21040 specpdl_ref count1 = SPECPDL_INDEX (); (gdb) 21042 specbind (Qinhibit_quit, Qt); (gdb) 21043 display_mode_lines (w); (gdb) 21044 unbind_to (count1, Qnil); (gdb) 21048 if (window_wants_mode_line (w) (gdb) 21059 if (window_wants_tab_line (w) (gdb) 21070 if (window_wants_header_line (w) (gdb) 21079 if (f->fonts_changed) (gdb) 21083 if (!line_number_displayed && w->base_line_pos != -1) (gdb) 21093 if (update_mode_line (gdb) 21098 if (FRAME_WINDOW_P (f)) (gdb) 21101 redisplay_menu_p = FRAME_EXTERNAL_MENU_BAR (f); (gdb) 21109 if (redisplay_menu_p) (gdb) 21110 display_menu_bar (w); (gdb) 21113 if (FRAME_WINDOW_P (f)) (gdb) 21115 if (WINDOWP (f->tab_bar_window) (gdb) 21122 if (FRAME_EXTERNAL_TOOL_BAR (f)) (gdb) 21123 update_frame_tool_bar (f); (gdb) 21138 gui_consider_frame_title (w->frame); (gdb) 21146 if (FRAME_WINDOW_P (f) (gdb) 21164 if (WINDOW_BOTTOM_DIVIDER_WIDTH (w)) (gdb) 493 return XUNTAG (a, Lisp_Vectorlike, struct window); (gdb) 1623 return frame_dimension (f->bottom_divider_width); (gdb) 21176 if (WINDOW_HAS_VERTICAL_SCROLL_BAR (w) || WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w)) (gdb) 21178 if (WINDOW_HAS_VERTICAL_SCROLL_BAR (w)) (gdb) 21180 set_vertical_scroll_bar (w); (gdb) 21182 if (WINDOW_HAS_HORIZONTAL_SCROLL_BAR (w)) (gdb) 21188 if (FRAME_TERMINAL (f)->redeem_scroll_bar_hook) (gdb) 21189 (*FRAME_TERMINAL (f)->redeem_scroll_bar_hook) (w); (gdb) 21195 if (CHARPOS (opoint) < BEGV) (gdb) 21197 else if (CHARPOS (opoint) > ZV) (gdb) 21199 else if (ochars_modiff == CHARS_MODIFF) (gdb) 21200 TEMP_SET_PT_BOTH (CHARPOS (opoint), BYTEPOS (opoint)); (gdb) 21211 set_buffer_internal_1 (old); (gdb) 21214 if (CHARPOS (lpoint) <= ZV) (gdb) 21216 if (lchars_modiff == CHARS_MODIFF) (gdb) 21217 TEMP_SET_PT_BOTH (CHARPOS (lpoint), BYTEPOS (lpoint)); (gdb) 21222 unbind_to (count, Qnil); (gdb) redisplay_window_0 (window=window <at> entry=XIL(0x555555cdd175)) at /home/steve/src/emacs/emacs-master/src/xdisp.c:18126 18126 return Qnil; (gdb) c Continuing. Thread 1 "emacs" hit Breakpoint 4, redisplay_window (window=XIL(0x555555cdd175), just_this_one_p=just_this_one_p <at> entry=false) at /home/steve/src/emacs/emacs-master/src/xdisp.c:20269 20269 if ((w->optional_new_start || window_frozen_p (w)) (gdb) n 20309 if (!BASE_LINE_NUMBER_VALID_P (w)) (gdb) p w->force_start $12 = false (gdb) n 20311 w->base_line_number = 0; (gdb) 20317 if (w->force_start) (gdb) 20520 if (current_matrix_up_to_date_p (gdb) 20540 else if (w->start_at_line_beg (gdb) 20556 else if ((tem = try_window_id (w)) != 0) (gdb) 20570 else if (CHARPOS (startp) >= BEGV (gdb) 20677 if (!update_mode_line) (gdb) 20684 if ((0 < scroll_conservatively (gdb) 20732 init_iterator (&it, w, PT, PT_BYTE, NULL, DEFAULT_FACE_ID); (gdb) 20733 it.current_y = it.last_visible_y; (gdb) 20734 if (centering_position < 0) (gdb) 20736 ptrdiff_t margin_pos = CHARPOS (startp); (gdb) 20742 if (margin (gdb) 20760 scrolling_up = PT > margin_pos; (gdb) 20761 aggressive = (gdb) 20766 if (!MINI_WINDOW_P (w) (gdb) 20805 centering_position = window_box_height (w) / 2; (gdb) 20807 if (current_buffer->long_line_optimizations_p (gdb) 20820 move_it_vertically_backward (&it, centering_position); (gdb) 20829 if (it.current_y <= 0) (gdb) 20836 it.current_x = it.wrap_prefix_width = it.hpos = 0; (gdb) 20841 set_marker_both (w->start, Qnil, IT_CHARPOS (it), IT_BYTEPOS (it)); (gdb) 20844 startp = run_window_scroll_functions (window, it.current.pos); (gdb) 20849 itdata = bidi_shelve_cache (); (gdb) 20853 if (!current_matrix_up_to_date_p (gdb) 20863 use_desired_matrix = (try_window (window, startp, 0) == 1); (gdb) 20865 bidi_unshelve_cache (itdata, false); (gdb) 20870 if (f->fonts_changed) (gdb) 20878 if (w->cursor.vpos < 0) (gdb) 20924 if (w->cursor.vpos < 0) (gdb) 20977 if (!cursor_row_fully_visible_p (w, false, false, false)) (gdb) 21017 SET_TEXT_POS_FROM_MARKER (startp, w->start); (gdb) 21018 w->start_at_line_beg = (CHARPOS (startp) == BEGV (gdb) p w->start $13 = XIL(0x555555d2419d) (gdb) pr $13 #<marker at 671 in sample> (gdb) p ZV $14 = 793 (gdb) c Continuing. Thread 1 "emacs" hit Breakpoint 4, redisplay_window (window=XIL(0x555555cdd175), just_this_one_p=just_this_one_p <at> entry=false) at /home/steve/src/emacs/emacs-master/src/xdisp.c:20269 20269 if ((w->optional_new_start || window_frozen_p (w)) (gdb) c Continuing. Thread 1 "emacs" hit Breakpoint 4, redisplay_window (window=XIL(0x555555cdd175), just_this_one_p=just_this_one_p <at> entry=false) at /home/steve/src/emacs/emacs-master/src/xdisp.c:20269 20269 if ((w->optional_new_start || window_frozen_p (w)) (gdb) c Continuing. Thread 1 "emacs" received signal SIGPIPE, Broken pipe. 0x00007ffff31407ef in __GI___libc_write (fd=13, buf=0x555555d07070, nbytes=568) at ../sysdeps/unix/sysv/linux/write.c:26 warning: 26 ../sysdeps/unix/sysv/linux/write.c: No such file or directory (gdb) Continuing. [Thread 0x7fffdd7366c0 (LWP 16531) exited] [Thread 0x7fffdf6486c0 (LWP 16528) exited] [Thread 0x7fffdffd86c0 (LWP 16527) exited] [Thread 0x7fffefe110c0 (LWP 16526) exited] [Thread 0x7fffdecb86c0 (LWP 16529) exited] [New process 16526] [Inferior 1 (process 16526) exited normally] (gdb) As you can see, after hitting the window.c breakpoint and stepping over set_marker_both, window-start is at 793, i.e. point-max, which is the expected position after invoking `(recenter 0)'. After continuing and hitting the xdisp.c breakpoint, I stepped through the code and saw w->force_start get set to true, then stepped further until redisplay_window_0, then continued and hit the breakpoint again, and now w->force_start was false. Then I stepped further until SET_TEXT_POS_FROM_MARKER (startp, w->start), after which window-start was now at 671, the value displayed in the message after one iteration, indicating the unexpected recentering. I cannot tell what caused window-start to change from 793 to 671. Steve Berman
GNU bug tracking system
Copyright (C) 1999 Darren O. Benham,
1997,2003 nCipher Corporation Ltd,
1994-97 Ian Jackson.