unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Alan Mackenzie <acm@muc.de>
To: Eli Zaretskii <eliz@gnu.org>
Cc: emacs-devel@gnu.org
Subject: Re: "... the window start at a meaningless point within a line."
Date: Thu, 15 Oct 2015 18:16:43 +0000	[thread overview]
Message-ID: <20151015181642.GA6467@acm.fritz.box> (raw)
In-Reply-To: <83egheaj9e.fsf@gnu.org>

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 <acm@muc.de>

> > > 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;
 }
 \f
+
+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;
+}
+
+\f
 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).



  parent reply	other threads:[~2015-10-15 18:16 UTC|newest]

Thread overview: 57+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-09-30 20:45 "... the window start at a meaningless point within a line." Alan Mackenzie
2015-10-01  8:48 ` Eli Zaretskii
2015-10-01  9:41   ` Alan Mackenzie
2015-10-01 10:16     ` Eli Zaretskii
2015-10-01 11:02       ` Alan Mackenzie
2015-10-01 12:03         ` Eli Zaretskii
2015-10-01 16:35           ` Alan Mackenzie
2015-10-15 18:16           ` Alan Mackenzie [this message]
2015-10-15 20:14             ` Eli Zaretskii
2015-10-16  9:55               ` Alan Mackenzie
2015-10-16 10:19                 ` Eli Zaretskii
2015-10-16 15:19                   ` Alan Mackenzie
2015-10-16 17:26                     ` Eli Zaretskii
2015-10-16 20:46                       ` David Kastrup
2015-10-17  7:15                         ` Eli Zaretskii
2015-10-17  7:56                           ` David Kastrup
2015-10-16 21:14                       ` Alan Mackenzie
2015-10-16 17:35                 ` Eli Zaretskii
2015-10-16 18:12                   ` Alan Mackenzie
2015-10-16 18:23                     ` Eli Zaretskii
2015-10-16 18:36                       ` Eli Zaretskii
2015-10-16 20:12                       ` Alan Mackenzie
2015-10-17  8:33                         ` Eli Zaretskii
2015-10-17 11:57                           ` Alan Mackenzie
2015-10-17 12:34                             ` Eli Zaretskii
2015-10-17 13:31                               ` Eli Zaretskii
2015-10-17 14:22                                 ` Eli Zaretskii
2015-10-18 15:00                                   ` Alan Mackenzie
2015-10-18 17:44                                     ` Eli Zaretskii
2015-10-19 10:27                                       ` Alan Mackenzie
2015-10-27 13:40                                         ` Alan Mackenzie
2015-10-27 17:35                                           ` Alan Mackenzie
2015-10-27 18:33                                             ` Eli Zaretskii
2015-10-27 18:23                                           ` Eli Zaretskii
2015-10-28  8:58                                             ` Alan Mackenzie
2015-10-28 13:15                                             ` Alan Mackenzie
2015-10-31 13:21                                               ` Eli Zaretskii
2015-10-31 21:17                                                 ` Alan Mackenzie
2015-11-01  3:40                                                   ` Eli Zaretskii
2015-11-01 14:45                                                     ` Alan Mackenzie
2015-11-01 15:23                                                 ` Alan Mackenzie
2015-11-01 17:45                                                   ` Eli Zaretskii
2015-11-01 18:07                                                     ` Eli Zaretskii
2015-11-01 18:46                                                     ` Alan Mackenzie
2015-10-18 14:53                                 ` Alan Mackenzie
2015-10-18 17:46                                   ` Eli Zaretskii
2015-10-19 10:45                                     ` Alan Mackenzie
2015-10-19 10:56                                       ` Eli Zaretskii
2015-10-19 11:24                                         ` Alan Mackenzie
2015-10-19 11:28                                           ` Eli Zaretskii
2015-10-19 12:02                                             ` Alan Mackenzie
2015-10-19 12:33                                               ` Eli Zaretskii
2015-10-19 13:11                                                 ` Alan Mackenzie
2015-10-19 13:27                                                   ` Eli Zaretskii
2015-10-19 19:15                                                     ` Alan Mackenzie
2015-10-27 13:46                                                       ` Alan Mackenzie
2015-10-17 15:30                               ` Alan Mackenzie

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20151015181642.GA6467@acm.fritz.box \
    --to=acm@muc.de \
    --cc=eliz@gnu.org \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).