emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: "... the window start at a meaningless point within a line."


From: Alan Mackenzie
Subject: Re: "... the window start at a meaningless point within a line."
Date: Thu, 15 Oct 2015 18:16:43 +0000
User-agent: Mutt/1.5.23 (2014-03-12)

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: address@hidden
> > From: Alan Mackenzie <address@hidden>

> > > 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 <PageUp> and <PageDown>
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).



reply via email to

[Prev in Thread] Current Thread [Next in Thread]