From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.devel Subject: Re: "... the window start at a meaningless point within a line." Date: Thu, 15 Oct 2015 18:16:43 +0000 Message-ID: <20151015181642.GA6467@acm.fritz.box> References: <20150930204513.GA3174@acm.fritz.box> <83mvw39dp3.fsf@gnu.org> <20151001094138.GA2515@acm.fritz.box> <83h9maao7w.fsf@gnu.org> <20151001110204.GB2515@acm.fritz.box> <83egheaj9e.fsf@gnu.org> NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1444933007 3843 80.91.229.3 (15 Oct 2015 18:16:47 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Thu, 15 Oct 2015 18:16:47 +0000 (UTC) Cc: emacs-devel@gnu.org To: Eli Zaretskii Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Thu Oct 15 20:16:39 2015 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1Zmn4v-00068u-QL for ged-emacs-devel@m.gmane.org; Thu, 15 Oct 2015 20:16:38 +0200 Original-Received: from localhost ([::1]:49026 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Zmn4v-0006F6-7Y for ged-emacs-devel@m.gmane.org; Thu, 15 Oct 2015 14:16:37 -0400 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:49205) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Zmn3Z-0006BE-QA for emacs-devel@gnu.org; Thu, 15 Oct 2015 14:15:17 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Zmn3V-0000xC-Qy for emacs-devel@gnu.org; Thu, 15 Oct 2015 14:15:13 -0400 Original-Received: from mail.muc.de ([193.149.48.3]:53950) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Zmn3V-0000wO-7r for emacs-devel@gnu.org; Thu, 15 Oct 2015 14:15:09 -0400 Original-Received: (qmail 96736 invoked by uid 3782); 15 Oct 2015 18:15:06 -0000 Original-Received: from acm.muc.de (p5B146702.dip0.t-ipconnect.de [91.20.103.2]) by colin.muc.de (tmda-ofmipd) with ESMTP; Thu, 15 Oct 2015 20:15:06 +0200 Original-Received: (qmail 6582 invoked by uid 1000); 15 Oct 2015 18:16:43 -0000 Content-Disposition: inline In-Reply-To: <83egheaj9e.fsf@gnu.org> User-Agent: Mutt/1.5.23 (2014-03-12) X-Delivery-Agent: TMDA/1.1.12 (Macallan) X-Primary-Address: acm@muc.de X-detected-operating-system: by eggs.gnu.org: FreeBSD 9.x X-Received-From: 193.149.48.3 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 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.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:191673 Archived-At: Hello, Eli. On Thu, Oct 01, 2015 at 03:03:09PM +0300, Eli Zaretskii wrote: > > Date: Thu, 1 Oct 2015 11:02:04 +0000 > > Cc: emacs-devel@gnu.org > > From: Alan Mackenzie > > > The display engine always starts from the physical BOL when it lays > > > out buffer text. That's because only at a physical BOL it knows that > > > the window-relative X coordinate is zero. Otherwise, there's no > > > anchor for it to start at. > > OK, so such a change would be, at least somewhat, non-trivial. Could the > > supplied window start position, as in (set-window-start line-middle-pos > > nil t), not count as such an anchor? > It cannot act as an anchor, because the horizontal X coordinate is not > (and cannot be) known by the caller. > > > Invalidating this basic assumption will cause strange effects, > > > including the horizontal scrolling I mentioned. > > This horizontal scrolling (of a long line at the top of W3, when a > > scroll-down brings the beginning of the line onto the window) would be > > precisely what's wanted here. > Then go ahead and make the change. The display engine will cope. I'm > not sure what users will say, but that's not my problem here ;-) As expected, it has been more difficult than expected. Getting `vertical-motion' working right was a game of "whac-a-mole". I suspect I still have to modify one or two other primitives, such as `compute-motion'. I have a working proof of concept in a patch below. It is not ready for committing for several reasons. One is that I haven't fully grasped what SAVE_IT and RESTORE_IT do with the third parameter (bidi data, I think), so I've just used SAVE_IT everywhere without much thought. Indeed, I don't know any languages which aren't L->R. I was a bit surprised at having to write my own `line-end-position' which takes a position as parameter. Maybe I should add this as an &optional parameter to `line-end-position' and `line-beginning-position'. To exercise the patch below, it is best to bind and to follow-scroll-down/up. They just work better in follow mode, and are identical to the standard functions when follow mode is not active. Load a source file (say, an elisp one) with longish lines into a buffer, and with C-x 3, make 2, 3, or 4 side by side windows. Adjust the frame's and/or windows' boundaries so that there are adjacent windows with unequal widths (as reported by window-body-width); M-{ and M-} are your friends, here. Do M-x follow-mode. In the course of experimentation, it is pertinent to have the LH window sometimes narrower than the RH one, sometimes wider. Scroll the buffer such that a long line straddles the border between the two windows of unequal width. Note that there are no missing characters, and no repeated characters. Note also that `vertical-motion', and commands that use it, work properly (modulo remaining bugs) near the top of the RH window. Here's the prototype change: Fix Follow Mode to handle long lines split between windows properly. 1. Amend the windows code to allow a window to start at an arbitrary point, rather than the nearest BOL to it. 2. Enhance, and make visible, "struct it" handling code in xdisp.c. src/window.h (struct window): Add new field exact_start. src/window.c (Fset_window_start): Amend code and doc wrt new parameter `exactstart". (Fdelete_other_windows_internal, set_window_buffer, window_scroll_pixel_based) (window_scroll_line_based, Frecenter, Fmove_to_window_line): Set exact_start to false after certain window manipulations. (recenter): new variable non_exact_start. Use it to check whether a non-null recentering has occurred, and only when so, set window.start etc. (Fwindow_test_temp): A temporary diagnostic function which displays window.start and window.exact_start. (syms_of_window): Include Swindow_test_dump. src/dispextern.h (SAVE_IT, RESTORE_IT): Move here from xdisp.c. (forward_to_next_display_line_start, get_window_start_on_continuation_line) (reseat_at_window_start): New declarations. src/xdisp.c (SAVE_IT, RESTORE_IT): Removed (moved to dispextern.h). (forward_to_next_display_line_start): New function. (reseat_at_window_start): New function. (get_window_start_on_continuation_line): New function, extracted from ... (compute_window_start_on_continuation_line): Now calls the above function. src/indent.c (pos_line_end_position): New function. (maybe_move_to_exact_bol): New function. (Fvertical_motion): Save state of w->exact_start when "lending" the window to another buffer. When the semi-final position of `it' is on the first text line of a window with exact_start set, correct it's position to a visible BOL by calling maybe_move_to_exact_bol. lisp/follow.el (follow-select-if-visible-from-first, follow-redisplay): Call set-window-start with `exactstart' parameter set. diff --git a/lisp/follow.el b/lisp/follow.el index 938c59e..d2caf4b 100644 --- a/lisp/follow.el +++ b/lisp/follow.el @@ -993,7 +993,7 @@ follow-select-if-visible-from-first (save-window-excursion (let ((windows windows)) (while (and (not win) windows) - (set-window-start (car windows) (point) 'noforce) + (set-window-start (car windows) (point) 'noforce t) (setq end-pos-end-p (follow-calc-win-end (car windows))) (goto-char (car end-pos-end-p)) ;; Visible, if dest above end, or if eob is visible @@ -1046,7 +1046,7 @@ follow-redisplay windows try-first-start win old-win-start))))) (dolist (w windows) (unless (and preserve-win (eq w win)) - (set-window-start w start)) + (set-window-start w start nil t)) (setq start (car (follow-calc-win-end w)))))) (defun follow-estimate-first-window-start (windows win start) diff --git a/src/dispextern.h b/src/dispextern.h index e44b70b..5da365c 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -2679,6 +2679,29 @@ struct it inhibit_free_realized_faces = true; \ } while (false) +/* SAVE_IT and RESTORE_IT are called when we save a snapshot of the + iterator state and later restore it. This is needed because the + bidi iterator on bidi.c keeps a stacked cache of its states, which + is really a singleton. When we use scratch iterator objects to + move around the buffer, we can cause the bidi cache to be pushed or + popped, and therefore we need to restore the cache state when we + return to the original iterator. */ +#define SAVE_IT(ITCOPY, ITORIG, CACHE) \ + do { \ + if (CACHE) \ + bidi_unshelve_cache (CACHE, true); \ + ITCOPY = ITORIG; \ + CACHE = bidi_shelve_cache (); \ + } while (false) + +#define RESTORE_IT(pITORIG, pITCOPY, CACHE) \ + do { \ + if (pITORIG != pITCOPY) \ + *(pITORIG) = *(pITCOPY); \ + bidi_unshelve_cache (CACHE, false); \ + CACHE = NULL; \ + } while (false) + /* Bit-flags indicating what operation move_it_to should perform. */ enum move_operation_enum @@ -3217,6 +3240,7 @@ void init_iterator (struct it *, struct window *, ptrdiff_t, void init_iterator_to_row_start (struct it *, struct window *, struct glyph_row *); void start_display (struct it *, struct window *, struct text_pos); +bool forward_to_next_display_line_start (struct it *it); void move_it_vertically (struct it *, int); void move_it_vertically_backward (struct it *, int); void move_it_by_lines (struct it *, ptrdiff_t); @@ -3224,6 +3248,7 @@ void move_it_past_eol (struct it *); void move_it_in_display_line (struct it *it, ptrdiff_t to_charpos, int to_x, enum move_operation_enum op); +struct text_pos get_window_start_on_continuation_line (struct window *w); bool in_display_vector_p (struct it *); int frame_mode_line_height (struct frame *); extern bool redisplaying_p; @@ -3233,6 +3258,8 @@ extern Lisp_Object help_echo_object, previous_help_echo_string; extern ptrdiff_t help_echo_pos; extern int last_tool_bar_item; extern void reseat_at_previous_visible_line_start (struct it *); +extern void reseat_at_window_start (struct it *it); + extern Lisp_Object lookup_glyphless_char_display (int, struct it *); extern ptrdiff_t compute_display_string_pos (struct text_pos *, struct bidi_string_data *, diff --git a/src/indent.c b/src/indent.c index 584f217..95542a7 100644 --- a/src/indent.c +++ b/src/indent.c @@ -1957,6 +1957,183 @@ window_column_x (struct window *w, Lisp_Object window, return x; } + +/* Get the end of the text line on which POS is. */ +static ptrdiff_t +pos_line_end_position (struct text_pos *pos) +{ + ptrdiff_t pt = PT, pt_byte = PT_BYTE; + Lisp_Object eol; + + SET_PT_BOTH (pos->charpos, pos->bytepos); + eol = Fline_end_position (Qnil); + SET_PT_BOTH (pt, pt_byte); + return XINT (eol); +} + + +/* maybe_move_to_exact_bol + + IT is at an "xdisp" BOL on the top (text) line on the display, + having just been moved NLINES to get there. Move IT to the correct + "exact" BOL. L0_PHYS_EOL is the end of this (text) line. + XDISP_WSTART is the "xdisp" window start. + + An "exact BOL" is the beginning of a line as displayed. The name + "exact" comes from the name of the parameter to `set-window-start'. + An "xdisp BOL" is the beginning of a line as calculated by xdisp.c, + based on the current window width and the position of the previous + beginning of a text line (which isn't invisible). Exact and xdisp + BOLs are in slightly different places when a long text line + straddles the boundary between two follow mode windows which are of + unequal width. + + The "xdisp" window start is where window-start would be if + calculated by xdisp from an earlier \n. The "exact" window start + is the value of w->start (having been set by a call of + `set-window-start' with non-nil fourth parameter). The "xdisp" and + "exact" BOLs are those calculated from these window start + positions. */ + +static void +maybe_move_to_exact_bol (struct it *it, ptrdiff_t nlines, + struct text_pos *xdisp_ws) +{ + ptrdiff_t L0_phys_EOL = pos_line_end_position (xdisp_ws); + ptrdiff_t beginning, end; + ptrdiff_t ws = marker_position (it->w->start); + + if (nlines <= 0) + { + beginning = IT_CHARPOS (*it); + end = PT; + } + else + { + beginning = PT; + end = IT_CHARPOS (*it); + } + + if (beginning < L0_phys_EOL + && end >= ws) + { + struct it exact_it, xdisp_it; /* ITs pointing to exact and xdisp BOLs. */ + struct it pre_exact_it, post_exact_it; /* Point to the exact BOLs + before/after xdisp_it. */ + void *cache = NULL; + int excess_xdisp_BOLs = 0; + bool below = true; /* i.e., exact_it hasn't yet reached `it'. */ + bool put_it_back_a_BOL = false; /* Supplements the `excess_xdisp_BOLs + mechanism. */ + + /* Initialise `exact_it' and `xdisp_it' to the pertinent window starts. */ + SAVE_IT (exact_it, *it, cache); + reseat_at_window_start (&exact_it); + exact_it.current_x = exact_it.hpos = 0; + SAVE_IT (xdisp_it, exact_it, cache); + reseat_at_previous_visible_line_start (&xdisp_it); + while (IT_CHARPOS (xdisp_it) < CHARPOS (*xdisp_ws) + && forward_to_next_display_line_start (&xdisp_it)); + xdisp_it.current_x = xdisp_it.hpos = 0; + + if (IT_CHARPOS (*it) < ws) + { + below = false; /* exact_it is already past `it'. */ + SAVE_IT (post_exact_it, exact_it, cache); + } + + + if (beginning < marker_position (it->w->start)) + excess_xdisp_BOLs--; + if (beginning < CHARPOS (*xdisp_ws) + && CHARPOS (*xdisp_ws) <= end) + excess_xdisp_BOLs++; + + /* We "hop" `exact_it' and `xdisp_it' over eachother until we reach the + point where they coincide (usually the next text BOL) or we reach + `end'. We calculate the disparity in exact and xdisp BOLs as we go. + Note that exact and xdisp BOLs alternate strictly until the point of + coincidence. */ + if (CHARPOS (*xdisp_ws) < ws) + goto xdisp_ws_is_earlier; + while (1) + { + if (IT_CHARPOS (exact_it) < IT_CHARPOS (*it)) + SAVE_IT (pre_exact_it, exact_it, cache); + if (!forward_to_next_display_line_start (&exact_it)) + break; /* protection against infinite looping. */ + if (below + && IT_CHARPOS (exact_it) >= IT_CHARPOS (*it)) + { + below = false; + SAVE_IT (post_exact_it, exact_it, cache); + } + if (IT_CHARPOS (exact_it) > end) + break; + if (IT_CHARPOS (exact_it) == IT_CHARPOS (xdisp_it)) + { + if (nlines <= 0) + excess_xdisp_BOLs--; + put_it_back_a_BOL = true; + break; + } + if (IT_CHARPOS (exact_it) > beginning) + excess_xdisp_BOLs--; + + xdisp_ws_is_earlier: + if (!forward_to_next_display_line_start (&xdisp_it)) + break; /* No infinite looping. */ + if (IT_CHARPOS (xdisp_it) > end) + break; + if (IT_CHARPOS (xdisp_it) == IT_CHARPOS (exact_it)) + { + excess_xdisp_BOLs++; + if (IT_CHARPOS (xdisp_it) == IT_CHARPOS (*it)) + { + SAVE_IT (pre_exact_it, exact_it, cache); + SAVE_IT (post_exact_it, exact_it, cache); + forward_to_next_display_line_start (&post_exact_it); + } + break; /* Will happen after a physical EOL. */ + } + if (IT_CHARPOS (xdisp_it) > beginning) + excess_xdisp_BOLs++; + } + + /* Decide which exact BOL to move `it' to, depending on `excess_xdisp_BOLs' + and possibly `put_it_back_a_BOL'. */ + /* First, is `it' off the top of the display? */ + if (excess_xdisp_BOLs + && IT_CHARPOS (*it) < ws) + { + forward_to_next_display_line_start (it); + if (IT_CHARPOS (*it) >= ws) + SAVE_IT (*it, post_exact_it, cache); + } + /* Is `it' within the initial section of text line 0 on the + screen, in which the "exact" and "xdisp" BOLs differ? */ + else if (IT_CHARPOS (*it) >= ws + && (IT_CHARPOS (*it) <= + (IT_CHARPOS (exact_it) == IT_CHARPOS (xdisp_it) + ? IT_CHARPOS (exact_it) + : end))) + if (excess_xdisp_BOLs == 1 /* Can only happen with nlines > 0. */ + || excess_xdisp_BOLs == -1) /* ... with nlines <= 0. */ + SAVE_IT (*it, post_exact_it, cache); + else + SAVE_IT (*it, pre_exact_it, cache); + /* Or is `it' at or beyond the point of coincidence of exact and xdisp + BOLs? */ + else + if (excess_xdisp_BOLs == 0 + && put_it_back_a_BOL) + move_it_by_lines (it, -1); + else if (excess_xdisp_BOLs != 0 + && !put_it_back_a_BOL) + forward_to_next_display_line_start (it); + } +} + DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 3, 0, doc: /* Move point to start of the screen line LINES lines down. If LINES is negative, this means moving up. @@ -1998,6 +2175,7 @@ whether or not it is currently displayed in some window. */) struct window *w; Lisp_Object old_buffer; EMACS_INT old_charpos IF_LINT (= 0), old_bytepos IF_LINT (= 0); + bool old_exact_start = false; Lisp_Object lcols; void *itdata = NULL; @@ -2019,9 +2197,11 @@ whether or not it is currently displayed in some window. */) old_buffer = w->contents; old_charpos = marker_position (w->pointm); old_bytepos = marker_byte_position (w->pointm); + old_exact_start = w->exact_start; wset_buffer (w, Fcurrent_buffer ()); set_marker_both (w->pointm, w->contents, BUF_PT (current_buffer), BUF_PT_BYTE (current_buffer)); + w->exact_start = false; } if (noninteractive) @@ -2041,8 +2221,10 @@ whether or not it is currently displayed in some window. */) double start_col; int start_x IF_LINT (= 0); int to_x = -1; + struct text_pos xdisp_ws; bool start_x_given = !NILP (cur_col); + if (start_x_given) { start_col = extract_float (cur_col); @@ -2205,6 +2387,16 @@ whether or not it is currently displayed in some window. */) } } + /* If our actual window start isn't where xdisp.c expects it to + be, and IT has ended up on the first text line on the + screen, amend IT's position to compensate. */ + if (w->exact_start) + { + xdisp_ws = get_window_start_on_continuation_line (w); + if (marker_position (w->start) != xdisp_ws.charpos) + maybe_move_to_exact_bol (&it, nlines, &xdisp_ws); + } + /* Move to the goal column, if one was specified. If the window was originally hscrolled, the goal column is interpreted as an addition to the hscroll amount. */ @@ -2240,6 +2432,7 @@ whether or not it is currently displayed in some window. */) wset_buffer (w, old_buffer); set_marker_both (w->pointm, w->contents, old_charpos, old_bytepos); + w->exact_start = old_exact_start; } return make_number (it.vpos); diff --git a/src/window.c b/src/window.c index d61f586..c77de33 100644 --- a/src/window.c +++ b/src/window.c @@ -1655,12 +1655,14 @@ Return POS. */) return pos; } -DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 3, 0, +DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 4, 0, doc: /* Make display in WINDOW start at position POS in WINDOW's buffer. WINDOW must be a live window and defaults to the selected one. Return POS. Optional third arg NOFORCE non-nil inhibits next redisplay from -overriding motion of point in order to display at this exact start. */) - (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce) +overriding motion of point in order to display at this exact +start. Optional fourth argument EXACTSTART non-nil prevents Emacs from +repositioning the window to the beginning of a line. */) + (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce, Lisp_Object exactstart) { register struct window *w = decode_live_window (window); @@ -1672,6 +1674,7 @@ overriding motion of point in order to display at this exact start. */) w->update_mode_line = true; /* Bug#15957. */ w->window_end_valid = false; + w->exact_start = !NILP (exactstart); wset_redisplay (w); return pos; @@ -3079,7 +3082,8 @@ window-start value is reasonable when this function is called. */) set_marker_both (w->start, w->contents, pos.bufpos, pos.bytepos); w->window_end_valid = false; w->start_at_line_beg = (pos.bytepos == BEGV_BYTE - || FETCH_BYTE (pos.bytepos - 1) == '\n'); + || FETCH_BYTE (pos.bytepos - 1) == '\n'); + w->exact_start = false; /* We need to do this, so that the window-scroll-functions get called. */ w->optional_new_start = true; @@ -3268,6 +3272,7 @@ set_window_buffer (Lisp_Object window, Lisp_Object buffer, set_marker_restricted (w->start, make_number (b->last_window_start), buffer); + w->exact_start = false; w->start_at_line_beg = false; w->force_start = false; } @@ -4819,6 +4824,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) spos = min (XINT (Fline_end_position (Qnil)) + 1, ZV); set_marker_restricted (w->start, make_number (spos), w->contents); + w->exact_start = false; w->start_at_line_beg = true; w->update_mode_line = true; /* Set force_start so that redisplay_window will run the @@ -4872,7 +4878,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) dy) * n; /* 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, + 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) { @@ -4950,7 +4956,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) vscrolled = true; } - if (! vscrolled) + if (!vscrolled && n) { ptrdiff_t pos = IT_CHARPOS (it); ptrdiff_t bytepos; @@ -4966,6 +4972,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) /* Set the window start, and set up the window for redisplay. */ set_marker_restricted_both (w->start, w->contents, IT_CHARPOS (it), IT_BYTEPOS (it)); + w->exact_start = false; bytepos = marker_byte_position (w->start); w->start_at_line_beg = (pos == BEGV || FETCH_BYTE (bytepos - 1) == '\n'); w->update_mode_line = true; @@ -4994,7 +5001,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) 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 - && (NILP (Vscroll_preserve_screen_position) + && (NILP (Vscroll_preserve_screen_position) || EQ (Vscroll_preserve_screen_position, Qt))) /* We found PT at a legitimate height. Leave it alone. */ ; @@ -5079,9 +5086,9 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) } if (charpos == PT && !partial_p - && (NILP (Vscroll_preserve_screen_position) + && (NILP (Vscroll_preserve_screen_position) || EQ (Vscroll_preserve_screen_position, Qt))) - /* We found PT before we found the display margin, so PT is ok. */ + /* We found PT before we found the display margin, so PT is ok. */ ; else if (window_scroll_pixel_based_preserve_y >= 0) { @@ -5121,7 +5128,7 @@ window_scroll_pixel_based (Lisp_Object window, int n, bool whole, bool noerror) /* Implementation of window_scroll that works based on screen lines. - See the comment of window_scroll for parameter descriptions. */ + See the comment of window_scroll for parameter descriptions. */ static void window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror) @@ -5131,7 +5138,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror) fontification, which in turn can modify buffer text (e.g., if the fontification functions replace escape sequences with faces, as in `grep-mode-font-lock-keywords'). So we use a marker to record - the old point position, to prevent crashes in SET_PT_BOTH. */ + the old point position, to prevent crashes in SET_PT_BOTH. */ Lisp_Object opoint_marker = Fpoint_marker (); register ptrdiff_t pos, pos_byte; register int ht = window_internal_height (w); @@ -5201,6 +5208,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror) max (0, min (scroll_margin, w->total_lines / 4)); set_marker_restricted_both (w->start, w->contents, pos, pos_byte); + if (n) w->exact_start = false; w->start_at_line_beg = !NILP (bolp); w->update_mode_line = true; /* Set force_start so that redisplay_window will run @@ -5214,7 +5222,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror) Fvertical_motion (original_pos, window, Qnil); } /* If we scrolled forward, put point enough lines down - that it is outside the scroll margin. */ + that it is outside the scroll margin. */ else if (n > 0) { int top_margin; @@ -5244,7 +5252,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror) int bottom_margin; /* If we scrolled backward, put point near the end of the window - but not within the scroll margin. */ + but not within the scroll margin. */ SET_PT_BOTH (pos, pos_byte); tem = Fvertical_motion (make_number (ht - this_scroll_margin), window, Qnil); @@ -5289,7 +5297,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror) screen-full which is defined as the height of the window minus next_screen_context_lines. If N is the symbol `-', scroll. DIRECTION may be 1 meaning to scroll down, or -1 meaning to scroll - up. This is the guts of Fscroll_up and Fscroll_down. */ + up. This is the guts of Fscroll_up and Fscroll_down. */ static void scroll_command (Lisp_Object n, int direction) @@ -5325,7 +5333,7 @@ If ARG is omitted or nil, scroll upward by a near full screen. A near full screen is `next-screen-context-lines' less than a full screen. Negative ARG means scroll downward. If ARG is the atom `-', scroll downward by nearly full screen. -When calling from a program, supply as argument a number, nil, or `-'. */) +When calling from a program, supply as argument a number, nil, or `-'. */) (Lisp_Object arg) { scroll_command (arg, 1); @@ -5338,7 +5346,7 @@ If ARG is omitted or nil, scroll down by a near full screen. A near full screen is `next-screen-context-lines' less than a full screen. Negative ARG means scroll upward. If ARG is the atom `-', scroll upward by nearly full screen. -When calling from a program, supply as argument a number, nil, or `-'. */) +When calling from a program, supply as argument a number, nil, or `-'. */) (Lisp_Object arg) { scroll_command (arg, -1); @@ -5371,12 +5379,12 @@ specifies the window. This takes precedence over else { /* Nothing specified; look for a neighboring window on the same - frame. */ + frame. */ window = Fnext_window (selected_window, Qnil, Qnil); if (EQ (window, selected_window)) /* That didn't get us anywhere; look for a window on another - visible frame. */ + visible frame. */ do window = Fnext_window (window, Qnil, Qt); while (! FRAME_VISIBLE_P (XFRAME (WINDOW_FRAME (XWINDOW (window)))) @@ -5580,6 +5588,12 @@ and redisplay normally--don't erase and redraw the frame. */) ptrdiff_t charpos, bytepos; EMACS_INT iarg IF_LINT (= 0); int this_scroll_margin; + struct text_pos non_exact_start; + + if (w->exact_start) + non_exact_start = get_window_start_on_continuation_line (w); + else + SET_TEXT_POS_FROM_MARKER (non_exact_start, w->start); if (buf != current_buffer) error ("`recenter'ing a window that does not display current-buffer."); @@ -5761,16 +5775,20 @@ and redisplay normally--don't erase and redraw the frame. */) bytepos = pos.bytepos; } - /* Set the new window start. */ - set_marker_both (w->start, w->contents, charpos, bytepos); - w->window_end_valid = false; + /* Set the new window start if we actually scrolled. */ + if (charpos != CHARPOS (non_exact_start)) + { + set_marker_both (w->start, w->contents, charpos, bytepos); + w->exact_start = false; + w->window_end_valid = false; - w->optional_new_start = true; + w->optional_new_start = true; - w->start_at_line_beg = (bytepos == BEGV_BYTE - || FETCH_BYTE (bytepos - 1) == '\n'); + w->start_at_line_beg = (bytepos == BEGV_BYTE + || FETCH_BYTE (bytepos - 1) == '\n'); - wset_redisplay (w); + wset_redisplay (w); + } return Qnil; } @@ -5845,6 +5863,7 @@ zero means top of window, negative means relative to bottom of window. */) int height = window_internal_height (w); Fvertical_motion (make_number (- (height / 2)), window, Qnil); set_marker_both (w->start, w->contents, PT, PT_BYTE); + w->exact_start = false; w->start_at_line_beg = !NILP (Fbolp ()); w->force_start = true; } @@ -7111,6 +7130,22 @@ and scrolling positions. */) return Qnil; } + +DEFUN ("window-test-dump", Fwindow_test_dump, Swindow_test_dump, 0, 0, "", + doc: /* Dump some critical components of the selected window to `message'.*/) + () +{ + Lisp_Object window = Fselected_window (); + struct window *w = decode_live_window (window); + AUTO_STRING (format, "start: %s; exact_start: %s"); + + CALLN (Fmessage, format, + w->start, + w->exact_start ? Qt : Qnil); + return Qnil; +} + + void init_window_once (void) { @@ -7320,8 +7355,8 @@ pixelwise even if this option is nil. */); window_resize_pixelwise = false; DEFVAR_BOOL ("fast-but-imprecise-scrolling", - Vfast_but_imprecise_scrolling, - doc: /* When non-nil, accelerate scrolling operations. + Vfast_but_imprecise_scrolling, + doc: /* When non-nil, accelerate scrolling operations. This comes into play when scrolling rapidly over previously unfontified buffer regions. Only those portions of the buffer which are actually going to be displayed get fontified. @@ -7439,6 +7474,7 @@ displayed after a scrolling operation to be somewhat inaccurate. */); defsubr (&Swindow_parameters); defsubr (&Swindow_parameter); defsubr (&Sset_window_parameter); + defsubr (&Swindow_test_dump); } void diff --git a/src/window.h b/src/window.h index eaff57e..5084863 100644 --- a/src/window.h +++ b/src/window.h @@ -383,6 +383,10 @@ struct window window. */ bool_bf suspend_auto_hscroll : 1; + /* True when the position in ->start is the exact window start pos, and + is not to be rounded to a beginning of line. */ + bool_bf exact_start : 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 863d891..b73d227 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -511,28 +511,28 @@ bool help_echo_showing_p; #define TEXT_PROP_DISTANCE_LIMIT 100 -/* SAVE_IT and RESTORE_IT are called when we save a snapshot of the - iterator state and later restore it. This is needed because the - bidi iterator on bidi.c keeps a stacked cache of its states, which - is really a singleton. When we use scratch iterator objects to - move around the buffer, we can cause the bidi cache to be pushed or - popped, and therefore we need to restore the cache state when we - return to the original iterator. */ -#define SAVE_IT(ITCOPY, ITORIG, CACHE) \ - do { \ - if (CACHE) \ - bidi_unshelve_cache (CACHE, true); \ - ITCOPY = ITORIG; \ - CACHE = bidi_shelve_cache (); \ - } while (false) - -#define RESTORE_IT(pITORIG, pITCOPY, CACHE) \ - do { \ - if (pITORIG != pITCOPY) \ - *(pITORIG) = *(pITCOPY); \ - bidi_unshelve_cache (CACHE, false); \ - CACHE = NULL; \ - } while (false) +/* /\* SAVE_IT and RESTORE_IT are called when we save a snapshot of the */ +/* iterator state and later restore it. This is needed because the */ +/* bidi iterator on bidi.c keeps a stacked cache of its states, which */ +/* is really a singleton. When we use scratch iterator objects to */ +/* move around the buffer, we can cause the bidi cache to be pushed or */ +/* popped, and therefore we need to restore the cache state when we */ +/* return to the original iterator. *\/ */ +/* #define SAVE_IT(ITCOPY, ITORIG, CACHE) \ */ +/* do { \ */ +/* if (CACHE) \ */ +/* bidi_unshelve_cache (CACHE, true); \ */ +/* ITCOPY = ITORIG; \ */ +/* CACHE = bidi_shelve_cache (); \ */ +/* } while (false) */ + +/* #define RESTORE_IT(pITORIG, pITCOPY, CACHE) \ */ +/* do { \ */ +/* if (pITORIG != pITCOPY) \ */ +/* *(pITORIG) = *(pITCOPY); \ */ +/* bidi_unshelve_cache (CACHE, false); \ */ +/* CACHE = NULL; \ */ +/* } while (false) */ /* Functions to mark elements as needing redisplay. */ enum { REDISPLAY_SOME = 2}; /* Arbitrary choice. */ @@ -6239,6 +6239,26 @@ forward_to_next_line_start (struct it *it, bool *skipped_p, return newline_found_p; } +/* Move IT to the start of the next display line. + The return value is true if the beginning of the next line was reached. +*/ + +bool +forward_to_next_display_line_start (struct it *it) +{ + enum move_it_result eres; + bool result = false; + eres = move_it_in_display_line_to (it, ZV, -1, MOVE_TO_POS); + if (eres != MOVE_POS_MATCH_OR_ZV) + { + if (eres != MOVE_LINE_CONTINUED) + set_iterator_to_next (it, false); + result = true; + it->current_x = it->hpos = 0; + } + return result; +} + /* Set IT's current position to the previous visible line start. Skip invisible text that is so either due to text properties or due to @@ -6342,6 +6362,17 @@ reseat_at_previous_visible_line_start (struct it *it) } +/* Reseat iterator IT at the beginning of IT's window. This is particularly + useful when the window's `exact_start' flag is set. */ + +void +reseat_at_window_start (struct it *it) +{ + SET_TEXT_POS_FROM_MARKER (it->current.pos, it->w->start); + reseat (it, it->current.pos, true); + CHECK_IT (it); +} + /* Reseat iterator IT on the next visible line start in the current buffer. ON_NEWLINE_P means position IT on the newline preceding the line start. Skip over invisible text that is so @@ -15271,13 +15302,13 @@ try_scrolling (Lisp_Object window, bool just_this_one_p, from the start of the continued line. It is the start of the screen line with the minimum distance from the old start W->start. */ -static bool -compute_window_start_on_continuation_line (struct window *w) +struct text_pos +get_window_start_on_continuation_line (struct window *w) { struct text_pos pos, start_pos; - bool window_start_changed_p = false; SET_TEXT_POS_FROM_MARKER (start_pos, w->start); + pos = start_pos; /* If window start is on a continuation line... Window start may be < BEGV in case there's invisible text at the start of the @@ -15302,7 +15333,7 @@ compute_window_start_on_continuation_line (struct window *w) reseat_at_previous_visible_line_start (&it); /* If the line start is "too far" away from the window start, - say it takes too much time to compute a new window start. */ + say it takes too much time to compute a new window start. */ if (CHARPOS (start_pos) - IT_CHARPOS (it) /* PXW: Do we need upper bounds here? */ < WINDOW_TOTAL_LINES (w) * WINDOW_TOTAL_COLS (w)) @@ -15342,17 +15373,30 @@ compute_window_start_on_continuation_line (struct window *w) else move_it_by_lines (&it, 1); } + } + } - /* Set the window start there. */ + return pos; +} + +static bool +compute_window_start_on_continuation_line (struct window *w) +{ + struct text_pos pos; + bool window_start_changed_p = false; + + if (!w->exact_start) + { + pos = get_window_start_on_continuation_line (w); + if (CHARPOS (pos) != marker_position (w->start)) + { SET_MARKER_FROM_TEXT_POS (w->start, pos); window_start_changed_p = true; } } - return window_start_changed_p; } - /* Try cursor movement in case text has not changed in window WINDOW, with window start STARTP. Value is -- Alan Mackenzie (Nuremberg, Germany).