From f2295fbfceff3ef0db91c9c5b9e5155fefe12697 Mon Sep 17 00:00:00 2001 From: Po Lu Date: Sat, 4 Dec 2021 18:33:53 +0800 Subject: [PATCH] Make it possible for point to appear outside a window * src/window.h (struct window): New field `cursor_visible_p'. * src/xdisp.c (redisplay_internal, redisplay_window): Allow point to appear outside the window. (set_cursor_from_row): Set cursor_visible_p appropriately. (try_window_id) (try_cursor_movement): Disable optimization when point is allowed to be invisible. (display_and_set_cursor): Erase cursor if invisible. (try_window): Allow some things. (syms_of_xdisp) : New variable. --- src/window.h | 4 ++ src/xdisp.c | 194 ++++++++++++++++++++++++++++++++++++++------------- 2 files changed, 150 insertions(+), 48 deletions(-) diff --git a/src/window.h b/src/window.h index 2400c422c1..e9528ff02f 100644 --- a/src/window.h +++ b/src/window.h @@ -445,6 +445,10 @@ #define WINDOW_H_INCLUDED window. */ bool_bf suspend_auto_hscroll : 1; + /* True if the cursor is supposed to be visible in this + window. */ + bool_bf cursor_visible_p : 1; + /* Amount by which lines of this window are scrolled in y-direction (smooth scrolling). */ int vscroll; diff --git a/src/xdisp.c b/src/xdisp.c index 7ca3977200..7a0949d514 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -16118,6 +16118,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 @@ -16989,6 +16991,8 @@ set_cursor_from_row (struct window *w, struct glyph_row *row, comes from a text property, not from an overlay. */ bool string_from_text_prop = false; + w->cursor_visible_p = false; + /* Don't even try doing anything if called for a mode-line or header-line or tab-line row, since the rest of the code isn't prepared to deal with such calamities. */ @@ -17575,6 +17579,7 @@ set_cursor_from_row (struct window *w, struct glyph_row *row, CHARPOS (this_line_start_pos) = 0; } + w->cursor_visible_p = true; return true; } @@ -18183,6 +18188,10 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp, return rc; #endif + /* TODO: enable this optimization. */ + if (!keep_point_visible) + return CURSOR_MOVEMENT_CANNOT_BE_USED; + /* Previously, there was a check for Lisp integer in the if-statement below. Now, this field is converted to ptrdiff_t, thus zero means invalid position in a buffer. */ @@ -18770,6 +18779,8 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) clear_glyph_matrix (w->desired_matrix); } + debug_method_add (w, "real redisplay starts"); + /* Otherwise set up data on this window; select its buffer and point value. */ /* Really select the buffer, for the sake of buffer-local @@ -18964,34 +18975,39 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) if (w->cursor.vpos < 0) { - /* If point does not appear, try to move point so it does - appear. The desired matrix has been built above, so we - can use it here. First see if point is in invisible - text, and if so, move it to the first visible buffer - position past that. */ - struct glyph_row *r = NULL; - Lisp_Object invprop = - get_char_property_and_overlay (make_fixnum (PT), Qinvisible, - Qnil, NULL); - - if (TEXT_PROP_MEANS_INVISIBLE (invprop) != 0) + if (!keep_point_visible) + w->cursor_visible_p = false; + else { - ptrdiff_t alt_pt; - Lisp_Object invprop_end = - Fnext_single_char_property_change (make_fixnum (PT), Qinvisible, - Qnil, Qnil); + /* If point does not appear, try to move point so it does + appear. The desired matrix has been built above, so we + can use it here. First see if point is in invisible + text, and if so, move it to the first visible buffer + position past that. */ + struct glyph_row *r = NULL; + Lisp_Object invprop = + get_char_property_and_overlay (make_fixnum (PT), Qinvisible, + Qnil, NULL); + + if (TEXT_PROP_MEANS_INVISIBLE (invprop) != 0) + { + ptrdiff_t alt_pt; + Lisp_Object invprop_end = + Fnext_single_char_property_change (make_fixnum (PT), Qinvisible, + Qnil, Qnil); - if (FIXNATP (invprop_end)) - alt_pt = XFIXNAT (invprop_end); - else - alt_pt = ZV; - r = row_containing_pos (w, alt_pt, w->desired_matrix->rows, - NULL, 0); + if (FIXNATP (invprop_end)) + alt_pt = XFIXNAT (invprop_end); + else + alt_pt = ZV; + r = row_containing_pos (w, alt_pt, w->desired_matrix->rows, + NULL, 0); + } + if (r) + 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 (r) - 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)) @@ -19223,10 +19239,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) @@ -19253,9 +19271,12 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) clear_glyph_matrix (w->desired_matrix); } + if (!keep_point_visible) + goto maybe_try_window; + 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; @@ -19317,6 +19338,7 @@ redisplay_window (Lisp_Object window, bool just_this_one_p) /* Determine the window start relative to point. */ 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); @@ -19584,6 +19606,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: @@ -19591,6 +19660,12 @@ 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_visible_p + || 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 @@ -19801,6 +19876,8 @@ try_window (Lisp_Object window, struct text_pos pos, int flags) struct frame *f = XFRAME (w->frame); int cursor_vpos = w->cursor.vpos; + debug_method_add (w, "try_window"); + /* Make POS the new window start. */ set_marker_both (w->start, Qnil, CHARPOS (pos), BYTEPOS (pos)); @@ -19960,6 +20037,10 @@ try_window_reusing_current_matrix (struct window *w) return false; #endif + /* TODO: enable this optimization. */ + if (!keep_point_visible) + return false; + /* The variable new_start now holds the new window start. The old start `start' can be determined from the current matrix. */ SET_TEXT_POS_FROM_MARKER (new_start, w->start); @@ -20875,6 +20956,8 @@ #define GIVE_UP(X) return 0 row = row_containing_pos (w, PT, r0, NULL, 0); if (row) set_cursor_from_row (w, row, current_matrix, 0, 0, 0, 0); + else + w->cursor_visible_p = false; return 1; } } @@ -20915,6 +20998,8 @@ #define GIVE_UP(X) return 0 row = row_containing_pos (w, PT, r0, NULL, 0); if (row) set_cursor_from_row (w, row, current_matrix, 0, 0, 0, 0); + else + w->cursor_visible_p = false; return 2; } } @@ -21152,6 +21237,8 @@ #define GIVE_UP(X) return 0 last_unchanged_at_beg_row + 1, 0); if (row) set_cursor_from_row (w, row, w->current_matrix, 0, 0, 0, 0); + else + w->cursor_visible_p = false; } /* Start from first_unchanged_at_end_row looking for PT. */ @@ -21162,6 +21249,8 @@ #define GIVE_UP(X) return 0 if (row) set_cursor_from_row (w, row, w->current_matrix, delta, delta_bytes, dy, dvpos); + else + w->cursor_visible_p = false; } /* Give up if cursor was not found. */ @@ -21173,24 +21262,25 @@ #define GIVE_UP(X) return 0 } /* Don't let the cursor end in the scroll margins. */ - { - int this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS); - int cursor_height = MATRIX_ROW (w->desired_matrix, w->cursor.vpos)->height; - - if ((w->cursor.y < this_scroll_margin - && CHARPOS (start) > BEGV) - /* Old redisplay didn't take scroll margin into account at the bottom, - but then global-hl-line-mode doesn't scroll. KFS 2004-06-14 */ - || (w->cursor.y - + (cursor_row_fully_visible_p (w, false, true, true) - ? 1 - : cursor_height + this_scroll_margin)) > it.last_visible_y) - { - w->cursor.vpos = -1; - clear_glyph_matrix (w->desired_matrix); - return -1; - } - } + if (w->cursor_visible_p) + { + int this_scroll_margin = window_scroll_margin (w, MARGIN_IN_PIXELS); + int cursor_height = MATRIX_ROW (w->desired_matrix, w->cursor.vpos)->height; + + if ((w->cursor.y < this_scroll_margin + && CHARPOS (start) > BEGV) + /* Old redisplay didn't take scroll margin into account at the bottom, + but then global-hl-line-mode doesn't scroll. KFS 2004-06-14 */ + || (w->cursor.y + + (cursor_row_fully_visible_p (w, false, true, true) + ? 1 + : cursor_height + this_scroll_margin)) > it.last_visible_y) + { + w->cursor.vpos = -1; + clear_glyph_matrix (w->desired_matrix); + return -1; + } + } /* Scroll the display. Do it before changing the current matrix so that xterm.c doesn't get confused about where the cursor glyph is @@ -32387,6 +32477,10 @@ display_and_set_cursor (struct window *w, bool on, && new_cursor_width != w->phys_cursor_width))) erase_phys_cursor (w); + if (!w->cursor_visible_p + || 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 @@ -35491,6 +35585,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'. */); -- 2.33.1