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).
next prev 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).