aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAlan Mackenzie2015-11-11 12:02:48 +0000
committerAlan Mackenzie2015-11-11 12:02:48 +0000
commit2c56fc2a3f106a1286ad793eed9bfaafd09a7411 (patch)
tree6082e9df4733fb39a9d1423568a703c93f7ead62 /src
parent25775a12c5168b0494dff15639ac25d8e1017530 (diff)
downloademacs-2c56fc2a3f106a1286ad793eed9bfaafd09a7411.tar.gz
emacs-2c56fc2a3f106a1286ad793eed9bfaafd09a7411.zip
First commit to scratch/follow. Make Isearch work with Follow Mode, etc.
doc/lispref/window.texi (Basic Windows): Add paragraph defining "Group of Windows" and new @defun selected-window-group. (Window Start and End): Describe new &optional parameter GROUP and ...-group-function for window-start, window-end, set-window-start, and pos-visible-in-window-p. (Textual Scrolling) Describe the same for recenter. doc/lispref/positions.texi (Screen Lines): Describe the same for move-to-window-line. src/window.c (Fwindow_start, Fwindow_end, Fset_window_start) (Fpos_visible_in_window_p, Frecenter, Fmove_to_window_line): To each, add ar new optional parameter "group". At the beginning of each, check whether the corresponding ...-group-function is set to a function, and if so execute this function in place of the normal processing. (syms_of_window): Define symbols for the six new variables below. (window-start-group-function, window-end-group-function) (set-window-start-group-function, recenter-group-function) (pos-visible-in-window-p-group-function, move-to-window-line-group-function): New permanent local buffer local variables. src/keyboard.c (Fposn_at_point): Add extra parameter in call to Fpos_visible_in_window_p. lisp/window.el (selected-window-group-function): New permanent local buffer local variable. (selected-window-group): New function. lisp/follow.el (follow-mode): Set the ...-group-function variables at mode enable, kill them at mode disable. Add/remove follow-after-change to/from after-change-functions. (follow-start-end-invalid): New variable. (follow-redisplay): Manipulate follow-start-end-invalid. (follow-after-change, follow-window-start, follow-window-end) (follow-set-window-start, follow-pos-visible-in-window-p) (follow-move-to-window-line, follow-sit-for): New functions. lisp/isearch.el (isearch-call-message): New macro. (isearch-update, with-isearch-suspended, isearch-del-char) (isearch-search-and-update, isearch-ring-adjust): Invoke above new macro. (with-isearch-suspended): Rearrange code such that isearch-call-message is invoked before point is moved. (isearch-message): Add comment about where point must be at function call. (isearch-search): Remove call to isearch-message. (isearch-lazy-highlight-window-group): New variable. (isearch-lazy-highlight-new-loop): Unconditionally start idle timer. Move the battery of tests to ... (isearch-lazy-highlight-maybe-new-loop): New function, started by idle timer. Note: (sit-for 0) is still called. (isearch-lazy-highlight-update): Check membership of isearch-lazy-highlight-window-group. Don't set the `window' overlay property. (isearch-update, isearch-done, isearch-string-out-of-window) (isearch-back-into-window, isearch-lazy-highlight-maybe-new-loop) (isearch-lazy-highlight-search, isearch-lazy-highlight-update) (isearch-lazy-highlight-update): Call the six amended primitives (see src/window.c above) with the new `group' argument set to t, to cooperate with Follow Mode.
Diffstat (limited to 'src')
-rw-r--r--src/keyboard.c2
-rw-r--r--src/window.c772
2 files changed, 462 insertions, 312 deletions
diff --git a/src/keyboard.c b/src/keyboard.c
index 851207874db..c7733568e7f 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -10663,7 +10663,7 @@ The `posn-' functions access elements of such lists. */)
10663 if (NILP (window)) 10663 if (NILP (window))
10664 window = selected_window; 10664 window = selected_window;
10665 10665
10666 tem = Fpos_visible_in_window_p (pos, window, Qt); 10666 tem = Fpos_visible_in_window_p (pos, window, Qt, Qnil);
10667 if (!NILP (tem)) 10667 if (!NILP (tem))
10668 { 10668 {
10669 Lisp_Object x = XCAR (tem); 10669 Lisp_Object x = XCAR (tem);
diff --git a/src/window.c b/src/window.c
index 0ac76d41861..efb4c9b4f8c 100644
--- a/src/window.c
+++ b/src/window.c
@@ -1540,13 +1540,23 @@ WINDOW must be a live window and defaults to the selected one. */)
1540 return Fmarker_position (decode_live_window (window)->old_pointm); 1540 return Fmarker_position (decode_live_window (window)->old_pointm);
1541} 1541}
1542 1542
1543DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 1, 0, 1543DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 2, 0,
1544 doc: /* Return position at which display currently starts in WINDOW. 1544 doc: /* Return position at which display currently starts in WINDOW.
1545WINDOW must be a live window and defaults to the selected one. 1545WINDOW must be a live window and defaults to the selected one.
1546This is updated by redisplay or by calling `set-window-start'. */) 1546This is updated by redisplay or by calling `set-window-start'.
1547 (Lisp_Object window) 1547
1548If GROUP is non-nil, and WINDOW is part of a group of windows collectively
1549displaying a buffer (such as with Follow Mode), return the start position of
1550the group rather than of the individual WINDOW. This condition holds when
1551`window-start-group-function' is set to a function, in which case
1552`window-start' calls the function with the argument WINDOW, then returns its
1553result, instead of doing its normal processing. */)
1554 (Lisp_Object window, Lisp_Object group)
1548{ 1555{
1549 return Fmarker_position (decode_live_window (window)->start); 1556 return (!NILP (group)
1557 && FUNCTIONP (Vwindow_start_group_function))
1558 ? call1 (Vwindow_start_group_function, window)
1559 : Fmarker_position (decode_live_window (window)->start);
1550} 1560}
1551 1561
1552/* This is text temporarily removed from the doc string below. 1562/* This is text temporarily removed from the doc string below.
@@ -1560,7 +1570,7 @@ have been if redisplay had finished, do this:
1560 (vertical-motion (1- (window-height window)) window) 1570 (vertical-motion (1- (window-height window)) window)
1561 (point))") */ 1571 (point))") */
1562 1572
1563DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 2, 0, 1573DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 3, 0,
1564 doc: /* Return position at which display currently ends in WINDOW. 1574 doc: /* Return position at which display currently ends in WINDOW.
1565WINDOW must be a live window and defaults to the selected one. 1575WINDOW must be a live window and defaults to the selected one.
1566This is updated by redisplay, when it runs to completion. 1576This is updated by redisplay, when it runs to completion.
@@ -1569,65 +1579,77 @@ does not update this value.
1569Return nil if there is no recorded value. (This can happen if the 1579Return nil if there is no recorded value. (This can happen if the
1570last redisplay of WINDOW was preempted, and did not finish.) 1580last redisplay of WINDOW was preempted, and did not finish.)
1571If UPDATE is non-nil, compute the up-to-date position 1581If UPDATE is non-nil, compute the up-to-date position
1572if it isn't already recorded. */) 1582if it isn't already recorded.
1573 (Lisp_Object window, Lisp_Object update) 1583
1574{ 1584If GROUP is non-nil, and WINDOW is part of a group of windows collectively
1575 Lisp_Object value; 1585displaying a buffer (such as with Follow Mode), return the end position of
1576 struct window *w = decode_live_window (window); 1586the group rather than of the individual WINDOW. This condition holds when
1577 Lisp_Object buf; 1587`window-end-group-function' is set to a function, in which case `window-end'
1578 struct buffer *b; 1588calls the function with the two arguments WINDOW and UPDATE, then returns its
1579 1589result, instead of doing its normal processing. */)
1580 buf = w->contents; 1590 (Lisp_Object window, Lisp_Object update, Lisp_Object group)
1581 CHECK_BUFFER (buf); 1591{
1582 b = XBUFFER (buf); 1592 if (!NILP (group)
1583 1593 && FUNCTIONP (Vwindow_end_group_function))
1584 if (! NILP (update) 1594 return call2 (Vwindow_end_group_function, window, update);
1585 && (windows_or_buffers_changed 1595 {
1586 || !w->window_end_valid 1596 Lisp_Object value;
1587 || b->clip_changed 1597 struct window *w = decode_live_window (window);
1588 || b->prevent_redisplay_optimizations_p 1598 Lisp_Object buf;
1589 || window_outdated (w)) 1599 struct buffer *b;
1590 /* Don't call display routines if we didn't yet create any real 1600
1591 frames, because the glyph matrices are not yet allocated in 1601 buf = w->contents;
1592 that case. This could happen in some code that runs in the 1602 CHECK_BUFFER (buf);
1593 daemon during initialization (e.g., see bug#20565). */ 1603 b = XBUFFER (buf);
1594 && !(noninteractive || FRAME_INITIAL_P (WINDOW_XFRAME (w)))) 1604
1595 { 1605 if (! NILP (update)
1596 struct text_pos startp; 1606 && (windows_or_buffers_changed
1597 struct it it; 1607 || !w->window_end_valid
1598 struct buffer *old_buffer = NULL; 1608 || b->clip_changed
1599 void *itdata = NULL; 1609 || b->prevent_redisplay_optimizations_p
1600 1610 || window_outdated (w))
1601 /* Cannot use Fvertical_motion because that function doesn't 1611 /* Don't call display routines if we didn't yet create any real
1602 cope with variable-height lines. */ 1612 frames, because the glyph matrices are not yet allocated in
1603 if (b != current_buffer) 1613 that case. This could happen in some code that runs in the
1604 { 1614 daemon during initialization (e.g., see bug#20565). */
1605 old_buffer = current_buffer; 1615 && !(noninteractive || FRAME_INITIAL_P (WINDOW_XFRAME (w))))
1606 set_buffer_internal (b); 1616 {
1607 } 1617 struct text_pos startp;
1608 1618 struct it it;
1609 /* In case W->start is out of the range, use something 1619 struct buffer *old_buffer = NULL;
1610 reasonable. This situation occurred when loading a file with 1620 void *itdata = NULL;
1611 `-l' containing a call to `rmail' with subsequent other 1621
1612 commands. At the end, W->start happened to be BEG, while 1622 /* Cannot use Fvertical_motion because that function doesn't
1613 rmail had already narrowed the buffer. */ 1623 cope with variable-height lines. */
1614 CLIP_TEXT_POS_FROM_MARKER (startp, w->start); 1624 if (b != current_buffer)
1615 1625 {
1616 itdata = bidi_shelve_cache (); 1626 old_buffer = current_buffer;
1617 start_display (&it, w, startp); 1627 set_buffer_internal (b);
1618 move_it_vertically (&it, window_box_height (w)); 1628 }
1619 if (it.current_y < it.last_visible_y) 1629
1620 move_it_past_eol (&it); 1630 /* In case W->start is out of the range, use something
1621 value = make_number (IT_CHARPOS (it)); 1631 reasonable. This situation occurred when loading a file with
1622 bidi_unshelve_cache (itdata, false); 1632 `-l' containing a call to `rmail' with subsequent other
1623 1633 commands. At the end, W->start happened to be BEG, while
1624 if (old_buffer) 1634 rmail had already narrowed the buffer. */
1625 set_buffer_internal (old_buffer); 1635 CLIP_TEXT_POS_FROM_MARKER (startp, w->start);
1626 } 1636
1627 else 1637 itdata = bidi_shelve_cache ();
1628 XSETINT (value, BUF_Z (b) - w->window_end_pos); 1638 start_display (&it, w, startp);
1639 move_it_vertically (&it, window_box_height (w));
1640 if (it.current_y < it.last_visible_y)
1641 move_it_past_eol (&it);
1642 value = make_number (IT_CHARPOS (it));
1643 bidi_unshelve_cache (itdata, false);
1644
1645 if (old_buffer)
1646 set_buffer_internal (old_buffer);
1647 }
1648 else
1649 XSETINT (value, BUF_Z (b) - w->window_end_pos);
1629 1650
1630 return value; 1651 return value;
1652 }
1631} 1653}
1632 1654
1633DEFUN ("set-window-point", Fset_window_point, Sset_window_point, 2, 2, 0, 1655DEFUN ("set-window-point", Fset_window_point, Sset_window_point, 2, 2, 0,
@@ -1666,30 +1688,43 @@ Return POS. */)
1666 return pos; 1688 return pos;
1667} 1689}
1668 1690
1669DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 3, 0, 1691DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 4, 0,
1670 doc: /* Make display in WINDOW start at position POS in WINDOW's buffer. 1692 doc: /* Make display in WINDOW start at position POS in WINDOW's buffer.
1671WINDOW must be a live window and defaults to the selected one. Return 1693WINDOW must be a live window and defaults to the selected one. Return
1672POS. Optional third arg NOFORCE non-nil inhibits next redisplay from 1694POS. Optional third arg NOFORCE non-nil inhibits next redisplay from
1673overriding motion of point in order to display at this exact start. */) 1695overriding motion of point in order to display at this exact start.
1674 (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce) 1696
1675{ 1697If GROUP is non-nil, and WINDOW is part of a group of windows collectively
1676 register struct window *w = decode_live_window (window); 1698displaying a buffer (such as with Follow Mode), set the start position of
1699the group rather than of the individual WINDOW. This condition holds when
1700`set-window-start-group-function' is set to a function, in which case
1701`set-window-start' calls the function with the three arguments WINDOW, POS,
1702and NOFORCE, then returns its result, instead of doing its normal
1703processing. */)
1704 (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce, Lisp_Object group)
1705{
1706 if (!NILP (group)
1707 && FUNCTIONP (Vset_window_start_group_function))
1708 return call3 (Vset_window_start_group_function, window, pos, noforce);
1709 {
1710 register struct window *w = decode_live_window (window);
1677 1711
1678 set_marker_restricted (w->start, pos, w->contents); 1712 set_marker_restricted (w->start, pos, w->contents);
1679 /* This is not right, but much easier than doing what is right. */ 1713 /* This is not right, but much easier than doing what is right. */
1680 w->start_at_line_beg = false; 1714 w->start_at_line_beg = false;
1681 if (NILP (noforce)) 1715 if (NILP (noforce))
1682 w->force_start = true; 1716 w->force_start = true;
1683 wset_update_mode_line (w); 1717 wset_update_mode_line (w);
1684 /* Bug#15957. */ 1718 /* Bug#15957. */
1685 w->window_end_valid = false; 1719 w->window_end_valid = false;
1686 wset_redisplay (w); 1720 wset_redisplay (w);
1687 1721
1688 return pos; 1722 return pos;
1723 }
1689} 1724}
1690 1725
1691DEFUN ("pos-visible-in-window-p", Fpos_visible_in_window_p, 1726DEFUN ("pos-visible-in-window-p", Fpos_visible_in_window_p,
1692 Spos_visible_in_window_p, 0, 3, 0, 1727 Spos_visible_in_window_p, 0, 4, 0,
1693 doc: /* Return non-nil if position POS is currently on the frame in WINDOW. 1728 doc: /* Return non-nil if position POS is currently on the frame in WINDOW.
1694WINDOW must be a live window and defaults to the selected one. 1729WINDOW must be a live window and defaults to the selected one.
1695 1730
@@ -1709,9 +1744,21 @@ of the window. The remaining elements are omitted if the character after
1709POS is fully visible; otherwise, RTOP and RBOT are the number of pixels 1744POS is fully visible; otherwise, RTOP and RBOT are the number of pixels
1710off-window at the top and bottom of the screen line ("row") containing 1745off-window at the top and bottom of the screen line ("row") containing
1711POS, ROWH is the visible height of that row, and VPOS is the row number 1746POS, ROWH is the visible height of that row, and VPOS is the row number
1712(zero-based). */) 1747(zero-based).
1713 (Lisp_Object pos, Lisp_Object window, Lisp_Object partially) 1748
1714{ 1749If GROUP is non-nil, and WINDOW is part of a group of windows collectively
1750displaying a buffer (such as with Follow Mode), test whether POS is visible
1751in the group of windows rather than in the individual WINDOW. This
1752condition holds when `pos-visible-in-window-p-function' is set to a
1753function, in which case `pos-visible-in-window-p' calls the function with
1754the three arguments POS, WINDOW, and PARTIALLY, then returns its result,
1755instead of doing its normal processing. */)
1756 (Lisp_Object pos, Lisp_Object window, Lisp_Object partially, Lisp_Object group)
1757{
1758 if (!NILP (group)
1759 && FUNCTIONP (Vpos_visible_in_window_p_group_function))
1760 return call3 (Vpos_visible_in_window_p_group_function, pos, window, partially);
1761 {
1715 struct window *w; 1762 struct window *w;
1716 EMACS_INT posint; 1763 EMACS_INT posint;
1717 struct buffer *buf; 1764 struct buffer *buf;
@@ -1760,6 +1807,7 @@ POS, ROWH is the visible height of that row, and VPOS is the row number
1760 } 1807 }
1761 1808
1762 return in_window; 1809 return in_window;
1810 }
1763} 1811}
1764 1812
1765DEFUN ("window-line-height", Fwindow_line_height, 1813DEFUN ("window-line-height", Fwindow_line_height,
@@ -5185,7 +5233,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror)
5185 } 5233 }
5186 5234
5187 XSETFASTINT (tem, PT); 5235 XSETFASTINT (tem, PT);
5188 tem = Fpos_visible_in_window_p (tem, window, Qnil); 5236 tem = Fpos_visible_in_window_p (tem, window, Qnil, Qnil);
5189 5237
5190 if (NILP (tem)) 5238 if (NILP (tem))
5191 { 5239 {
@@ -5574,7 +5622,7 @@ displayed_window_lines (struct window *w)
5574} 5622}
5575 5623
5576 5624
5577DEFUN ("recenter", Frecenter, Srecenter, 0, 1, "P", 5625DEFUN ("recenter", Frecenter, Srecenter, 0, 2, "P\ni",
5578 doc: /* Center point in selected window and maybe redisplay frame. 5626 doc: /* Center point in selected window and maybe redisplay frame.
5579With a numeric prefix argument ARG, recenter putting point on screen line ARG 5627With a numeric prefix argument ARG, recenter putting point on screen line ARG
5580relative to the selected window. If ARG is negative, it counts up from the 5628relative to the selected window. If ARG is negative, it counts up from the
@@ -5588,208 +5636,221 @@ height needed); if `recenter-redisplay' has the special value `tty',
5588then only tty frames are redrawn. 5636then only tty frames are redrawn.
5589 5637
5590Just C-u as prefix means put point in the center of the window 5638Just C-u as prefix means put point in the center of the window
5591and redisplay normally--don't erase and redraw the frame. */) 5639and redisplay normally--don't erase and redraw the frame.
5592 (register Lisp_Object arg) 5640
5593{ 5641When `recenter' is called from a program, GROUP is non-nil, and WINDOW is
5594 struct window *w = XWINDOW (selected_window); 5642part of a group of windows collectively displaying a buffer (such as with
5595 struct buffer *buf = XBUFFER (w->contents); 5643Follow Mode), perform `recenter''s actions on the group rather than on the
5596 bool center_p = false; 5644individual WINDOW. This condition holds when `recenter-group-function' is
5597 ptrdiff_t charpos, bytepos; 5645set to a function, in which case `recenter' calls the function with the
5598 EMACS_INT iarg IF_LINT (= 0); 5646argument ARG, then returns its value, instead of doing its normal
5599 int this_scroll_margin; 5647processing. */)
5600 5648 (register Lisp_Object arg, Lisp_Object group)
5601 if (buf != current_buffer) 5649{
5602 error ("`recenter'ing a window that does not display current-buffer."); 5650 if (!NILP (group)
5651 && FUNCTIONP (Vrecenter_group_function))
5652 return call1 (Vrecenter_group_function, arg);
5653 {
5654 struct window *w = XWINDOW (selected_window);
5655 struct buffer *buf = XBUFFER (w->contents);
5656 bool center_p = false;
5657 ptrdiff_t charpos, bytepos;
5658 EMACS_INT iarg IF_LINT (= 0);
5659 int this_scroll_margin;
5603 5660
5604 /* If redisplay is suppressed due to an error, try again. */ 5661 if (buf != current_buffer)
5605 buf->display_error_modiff = 0; 5662 error ("`recenter'ing a window that does not display current-buffer.");
5606 5663
5607 if (NILP (arg)) 5664 /* If redisplay is suppressed due to an error, try again. */
5608 { 5665 buf->display_error_modiff = 0;
5609 if (!NILP (Vrecenter_redisplay)
5610 && (!EQ (Vrecenter_redisplay, Qtty)
5611 || !NILP (Ftty_type (selected_frame))))
5612 {
5613 ptrdiff_t i;
5614 5666
5615 /* Invalidate pixel data calculated for all compositions. */ 5667 if (NILP (arg))
5616 for (i = 0; i < n_compositions; i++) 5668 {
5617 composition_table[i]->font = NULL; 5669 if (!NILP (Vrecenter_redisplay)
5670 && (!EQ (Vrecenter_redisplay, Qtty)
5671 || !NILP (Ftty_type (selected_frame))))
5672 {
5673 ptrdiff_t i;
5674
5675 /* Invalidate pixel data calculated for all compositions. */
5676 for (i = 0; i < n_compositions; i++)
5677 composition_table[i]->font = NULL;
5618#if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS) 5678#if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS)
5619 WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1; 5679 WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1;
5620#endif 5680#endif
5621 Fredraw_frame (WINDOW_FRAME (w)); 5681 Fredraw_frame (WINDOW_FRAME (w));
5622 SET_FRAME_GARBAGED (WINDOW_XFRAME (w)); 5682 SET_FRAME_GARBAGED (WINDOW_XFRAME (w));
5623 } 5683 }
5624 5684
5685 center_p = true;
5686 }
5687 else if (CONSP (arg)) /* Just C-u. */
5625 center_p = true; 5688 center_p = true;
5626 } 5689 else
5627 else if (CONSP (arg)) /* Just C-u. */ 5690 {
5628 center_p = true; 5691 arg = Fprefix_numeric_value (arg);
5629 else 5692 CHECK_NUMBER (arg);
5630 { 5693 iarg = XINT (arg);
5631 arg = Fprefix_numeric_value (arg); 5694 }
5632 CHECK_NUMBER (arg);
5633 iarg = XINT (arg);
5634 }
5635
5636 /* Do this after making BUF current
5637 in case scroll_margin is buffer-local. */
5638 this_scroll_margin
5639 = max (0, min (scroll_margin, w->total_lines / 4));
5640
5641 /* Don't use redisplay code for initial frames, as the necessary
5642 data structures might not be set up yet then. */
5643 if (!FRAME_INITIAL_P (XFRAME (w->frame)))
5644 {
5645 if (center_p)
5646 {
5647 struct it it;
5648 struct text_pos pt;
5649 void *itdata = bidi_shelve_cache ();
5650
5651 SET_TEXT_POS (pt, PT, PT_BYTE);
5652 start_display (&it, w, pt);
5653 move_it_vertically_backward (&it, window_box_height (w) / 2);
5654 charpos = IT_CHARPOS (it);
5655 bytepos = IT_BYTEPOS (it);
5656 bidi_unshelve_cache (itdata, false);
5657 }
5658 else if (iarg < 0)
5659 {
5660 struct it it;
5661 struct text_pos pt;
5662 ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg);
5663 int extra_line_spacing;
5664 int h = window_box_height (w);
5665 int ht = window_internal_height (w);
5666 void *itdata = bidi_shelve_cache ();
5667
5668 nlines = clip_to_bounds (this_scroll_margin + 1, nlines,
5669 ht - this_scroll_margin);
5670
5671 SET_TEXT_POS (pt, PT, PT_BYTE);
5672 start_display (&it, w, pt);
5673
5674 /* Be sure we have the exact height of the full line containing PT. */
5675 move_it_by_lines (&it, 0);
5676
5677 /* The amount of pixels we have to move back is the window
5678 height minus what's displayed in the line containing PT,
5679 and the lines below. */
5680 it.current_y = 0;
5681 it.vpos = 0;
5682 move_it_by_lines (&it, nlines);
5683
5684 if (it.vpos == nlines)
5685 h -= it.current_y;
5686 else
5687 {
5688 /* Last line has no newline. */
5689 h -= line_bottom_y (&it);
5690 it.vpos++;
5691 }
5692
5693 /* Don't reserve space for extra line spacing of last line. */
5694 extra_line_spacing = it.max_extra_line_spacing;
5695
5696 /* If we can't move down NLINES lines because we hit
5697 the end of the buffer, count in some empty lines. */
5698 if (it.vpos < nlines)
5699 {
5700 nlines -= it.vpos;
5701 extra_line_spacing = it.extra_line_spacing;
5702 h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing);
5703 }
5704 if (h <= 0)
5705 {
5706 bidi_unshelve_cache (itdata, false);
5707 return Qnil;
5708 }
5709
5710 /* Now find the new top line (starting position) of the window. */
5711 start_display (&it, w, pt);
5712 it.current_y = 0;
5713 move_it_vertically_backward (&it, h);
5714
5715 /* If extra line spacing is present, we may move too far
5716 back. This causes the last line to be only partially
5717 visible (which triggers redisplay to recenter that line
5718 in the middle), so move forward.
5719 But ignore extra line spacing on last line, as it is not
5720 considered to be part of the visible height of the line.
5721 */
5722 h += extra_line_spacing;
5723 while (-it.current_y > h)
5724 move_it_by_lines (&it, 1);
5725
5726 charpos = IT_CHARPOS (it);
5727 bytepos = IT_BYTEPOS (it);
5728
5729 bidi_unshelve_cache (itdata, false);
5730 }
5731 else
5732 {
5733 struct it it;
5734 struct text_pos pt;
5735 ptrdiff_t nlines = min (PTRDIFF_MAX, iarg);
5736 int ht = window_internal_height (w);
5737 void *itdata = bidi_shelve_cache ();
5738
5739 nlines = clip_to_bounds (this_scroll_margin, nlines,
5740 ht - this_scroll_margin - 1);
5741
5742 SET_TEXT_POS (pt, PT, PT_BYTE);
5743 start_display (&it, w, pt);
5744
5745 /* Move to the beginning of screen line containing PT. */
5746 move_it_by_lines (&it, 0);
5747
5748 /* Move back to find the point which is ARG screen lines above PT. */
5749 if (nlines > 0)
5750 {
5751 it.current_y = 0;
5752 it.vpos = 0;
5753 move_it_by_lines (&it, -nlines);
5754 }
5755 5695
5756 charpos = IT_CHARPOS (it); 5696 /* Do this after making BUF current
5757 bytepos = IT_BYTEPOS (it); 5697 in case scroll_margin is buffer-local. */
5698 this_scroll_margin
5699 = max (0, min (scroll_margin, w->total_lines / 4));
5758 5700
5759 bidi_unshelve_cache (itdata, false); 5701 /* Don't use redisplay code for initial frames, as the necessary
5760 } 5702 data structures might not be set up yet then. */
5761 } 5703 if (!FRAME_INITIAL_P (XFRAME (w->frame)))
5762 else 5704 {
5763 { 5705 if (center_p)
5764 struct position pos; 5706 {
5765 int ht = window_internal_height (w); 5707 struct it it;
5708 struct text_pos pt;
5709 void *itdata = bidi_shelve_cache ();
5710
5711 SET_TEXT_POS (pt, PT, PT_BYTE);
5712 start_display (&it, w, pt);
5713 move_it_vertically_backward (&it, window_box_height (w) / 2);
5714 charpos = IT_CHARPOS (it);
5715 bytepos = IT_BYTEPOS (it);
5716 bidi_unshelve_cache (itdata, false);
5717 }
5718 else if (iarg < 0)
5719 {
5720 struct it it;
5721 struct text_pos pt;
5722 ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg);
5723 int extra_line_spacing;
5724 int h = window_box_height (w);
5725 int ht = window_internal_height (w);
5726 void *itdata = bidi_shelve_cache ();
5727
5728 nlines = clip_to_bounds (this_scroll_margin + 1, nlines,
5729 ht - this_scroll_margin);
5730
5731 SET_TEXT_POS (pt, PT, PT_BYTE);
5732 start_display (&it, w, pt);
5733
5734 /* Be sure we have the exact height of the full line containing PT. */
5735 move_it_by_lines (&it, 0);
5736
5737 /* The amount of pixels we have to move back is the window
5738 height minus what's displayed in the line containing PT,
5739 and the lines below. */
5740 it.current_y = 0;
5741 it.vpos = 0;
5742 move_it_by_lines (&it, nlines);
5743
5744 if (it.vpos == nlines)
5745 h -= it.current_y;
5746 else
5747 {
5748 /* Last line has no newline. */
5749 h -= line_bottom_y (&it);
5750 it.vpos++;
5751 }
5752
5753 /* Don't reserve space for extra line spacing of last line. */
5754 extra_line_spacing = it.max_extra_line_spacing;
5755
5756 /* If we can't move down NLINES lines because we hit
5757 the end of the buffer, count in some empty lines. */
5758 if (it.vpos < nlines)
5759 {
5760 nlines -= it.vpos;
5761 extra_line_spacing = it.extra_line_spacing;
5762 h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing);
5763 }
5764 if (h <= 0)
5765 {
5766 bidi_unshelve_cache (itdata, false);
5767 return Qnil;
5768 }
5769
5770 /* Now find the new top line (starting position) of the window. */
5771 start_display (&it, w, pt);
5772 it.current_y = 0;
5773 move_it_vertically_backward (&it, h);
5774
5775 /* If extra line spacing is present, we may move too far
5776 back. This causes the last line to be only partially
5777 visible (which triggers redisplay to recenter that line
5778 in the middle), so move forward.
5779 But ignore extra line spacing on last line, as it is not
5780 considered to be part of the visible height of the line.
5781 */
5782 h += extra_line_spacing;
5783 while (-it.current_y > h)
5784 move_it_by_lines (&it, 1);
5785
5786 charpos = IT_CHARPOS (it);
5787 bytepos = IT_BYTEPOS (it);
5788
5789 bidi_unshelve_cache (itdata, false);
5790 }
5791 else
5792 {
5793 struct it it;
5794 struct text_pos pt;
5795 ptrdiff_t nlines = min (PTRDIFF_MAX, iarg);
5796 int ht = window_internal_height (w);
5797 void *itdata = bidi_shelve_cache ();
5798
5799 nlines = clip_to_bounds (this_scroll_margin, nlines,
5800 ht - this_scroll_margin - 1);
5801
5802 SET_TEXT_POS (pt, PT, PT_BYTE);
5803 start_display (&it, w, pt);
5804
5805 /* Move to the beginning of screen line containing PT. */
5806 move_it_by_lines (&it, 0);
5807
5808 /* Move back to find the point which is ARG screen lines above PT. */
5809 if (nlines > 0)
5810 {
5811 it.current_y = 0;
5812 it.vpos = 0;
5813 move_it_by_lines (&it, -nlines);
5814 }
5815
5816 charpos = IT_CHARPOS (it);
5817 bytepos = IT_BYTEPOS (it);
5818
5819 bidi_unshelve_cache (itdata, false);
5820 }
5821 }
5822 else
5823 {
5824 struct position pos;
5825 int ht = window_internal_height (w);
5766 5826
5767 if (center_p) 5827 if (center_p)
5768 iarg = ht / 2; 5828 iarg = ht / 2;
5769 else if (iarg < 0) 5829 else if (iarg < 0)
5770 iarg += ht; 5830 iarg += ht;
5771 5831
5772 /* Don't let it get into the margin at either top or bottom. */ 5832 /* Don't let it get into the margin at either top or bottom. */
5773 iarg = clip_to_bounds (this_scroll_margin, iarg, 5833 iarg = clip_to_bounds (this_scroll_margin, iarg,
5774 ht - this_scroll_margin - 1); 5834 ht - this_scroll_margin - 1);
5775 5835
5776 pos = *vmotion (PT, PT_BYTE, - iarg, w); 5836 pos = *vmotion (PT, PT_BYTE, - iarg, w);
5777 charpos = pos.bufpos; 5837 charpos = pos.bufpos;
5778 bytepos = pos.bytepos; 5838 bytepos = pos.bytepos;
5779 } 5839 }
5780 5840
5781 /* Set the new window start. */ 5841 /* Set the new window start. */
5782 set_marker_both (w->start, w->contents, charpos, bytepos); 5842 set_marker_both (w->start, w->contents, charpos, bytepos);
5783 w->window_end_valid = false; 5843 w->window_end_valid = false;
5784 5844
5785 w->optional_new_start = true; 5845 w->optional_new_start = true;
5786 5846
5787 w->start_at_line_beg = (bytepos == BEGV_BYTE 5847 w->start_at_line_beg = (bytepos == BEGV_BYTE
5788 || FETCH_BYTE (bytepos - 1) == '\n'); 5848 || FETCH_BYTE (bytepos - 1) == '\n');
5789 5849
5790 wset_redisplay (w); 5850 wset_redisplay (w);
5791 5851
5792 return Qnil; 5852 return Qnil;
5853 }
5793} 5854}
5794 5855
5795DEFUN ("window-text-width", Fwindow_text_width, Swindow_text_width, 5856DEFUN ("window-text-width", Fwindow_text_width, Swindow_text_width,
@@ -5836,52 +5897,68 @@ pixels. */)
5836} 5897}
5837 5898
5838DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line, 5899DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line,
5839 1, 1, "P", 5900 1, 2, "P\ni",
5840 doc: /* Position point relative to window. 5901 doc: /* Position point relative to window.
5841ARG nil means position point at center of window. 5902ARG nil means position point at center of window.
5842Else, ARG specifies vertical position within the window; 5903Else, ARG specifies vertical position within the window;
5843zero means top of window, negative means relative to bottom of window. */) 5904zero means top of window, negative means relative to bottom of window.
5844 (Lisp_Object arg) 5905
5845{ 5906When GROUP is non-nil, and `move-to-window-line-group-function' is set to a
5846 struct window *w = XWINDOW (selected_window); 5907function, then instead of the above, that function is called with the
5847 int lines, start; 5908single argument ARG, and its result returned.
5848 Lisp_Object window; 5909
5910If GROUP is non-nil, and WINDOW is part of a group of windows collectively
5911displaying a buffer (such as with Follow Mode), position point relative to
5912the group of windows as a whole rather than the individual WINDOW. This
5913condition holds when `move-to-window-line-group-function' is set to a
5914function, in which case `move-to-window-line' calls the function with the
5915argument ARG, then returns its result, instead of doing its normal
5916processing. */)
5917 (Lisp_Object arg, Lisp_Object group)
5918{
5919 if (!NILP (group)
5920 && FUNCTIONP (Vmove_to_window_line_group_function))
5921 return call1 (Vmove_to_window_line_group_function, arg);
5922 {
5923 struct window *w = XWINDOW (selected_window);
5924 int lines, start;
5925 Lisp_Object window;
5849#if false 5926#if false
5850 int this_scroll_margin; 5927 int this_scroll_margin;
5851#endif 5928#endif
5852 5929
5853 if (!(BUFFERP (w->contents) && XBUFFER (w->contents) == current_buffer)) 5930 if (!(BUFFERP (w->contents) && XBUFFER (w->contents) == current_buffer))
5854 /* This test is needed to make sure PT/PT_BYTE make sense in w->contents 5931 /* This test is needed to make sure PT/PT_BYTE make sense in w->contents
5855 when passed below to set_marker_both. */ 5932 when passed below to set_marker_both. */
5856 error ("move-to-window-line called from unrelated buffer"); 5933 error ("move-to-window-line called from unrelated buffer");
5857 5934
5858 window = selected_window; 5935 window = selected_window;
5859 start = marker_position (w->start); 5936 start = marker_position (w->start);
5860 if (start < BEGV || start > ZV) 5937 if (start < BEGV || start > ZV)
5861 { 5938 {
5862 int height = window_internal_height (w); 5939 int height = window_internal_height (w);
5863 Fvertical_motion (make_number (- (height / 2)), window, Qnil); 5940 Fvertical_motion (make_number (- (height / 2)), window, Qnil);
5864 set_marker_both (w->start, w->contents, PT, PT_BYTE); 5941 set_marker_both (w->start, w->contents, PT, PT_BYTE);
5865 w->start_at_line_beg = !NILP (Fbolp ()); 5942 w->start_at_line_beg = !NILP (Fbolp ());
5866 w->force_start = true; 5943 w->force_start = true;
5867 } 5944 }
5868 else 5945 else
5869 Fgoto_char (w->start); 5946 Fgoto_char (w->start);
5870 5947
5871 lines = displayed_window_lines (w); 5948 lines = displayed_window_lines (w);
5872 5949
5873#if false 5950#if false
5874 this_scroll_margin = max (0, min (scroll_margin, lines / 4)); 5951 this_scroll_margin = max (0, min (scroll_margin, lines / 4));
5875#endif 5952#endif
5876 5953
5877 if (NILP (arg)) 5954 if (NILP (arg))
5878 XSETFASTINT (arg, lines / 2); 5955 XSETFASTINT (arg, lines / 2);
5879 else 5956 else
5880 { 5957 {
5881 EMACS_INT iarg = XINT (Fprefix_numeric_value (arg)); 5958 EMACS_INT iarg = XINT (Fprefix_numeric_value (arg));
5882 5959
5883 if (iarg < 0) 5960 if (iarg < 0)
5884 iarg = iarg + lines; 5961 iarg = iarg + lines;
5885 5962
5886#if false /* This code would prevent move-to-window-line from moving point 5963#if false /* This code would prevent move-to-window-line from moving point
5887 to a place inside the scroll margins (which would cause the 5964 to a place inside the scroll margins (which would cause the
@@ -5889,19 +5966,20 @@ zero means top of window, negative means relative to bottom of window. */)
5889 it is probably better not to install it. However, it is here 5966 it is probably better not to install it. However, it is here
5890 inside #if false so as not to lose it. -- rms. */ 5967 inside #if false so as not to lose it. -- rms. */
5891 5968
5892 /* Don't let it get into the margin at either top or bottom. */ 5969 /* Don't let it get into the margin at either top or bottom. */
5893 iarg = max (iarg, this_scroll_margin); 5970 iarg = max (iarg, this_scroll_margin);
5894 iarg = min (iarg, lines - this_scroll_margin - 1); 5971 iarg = min (iarg, lines - this_scroll_margin - 1);
5895#endif 5972#endif
5896 5973
5897 arg = make_number (iarg); 5974 arg = make_number (iarg);
5898 } 5975 }
5899 5976
5900 /* Skip past a partially visible first line. */ 5977 /* Skip past a partially visible first line. */
5901 if (w->vscroll) 5978 if (w->vscroll)
5902 XSETINT (arg, XINT (arg) + 1); 5979 XSETINT (arg, XINT (arg) + 1);
5903 5980
5904 return Fvertical_motion (arg, window, Qnil); 5981 return Fvertical_motion (arg, window, Qnil);
5982 }
5905} 5983}
5906 5984
5907 5985
@@ -7175,6 +7253,12 @@ syms_of_window (void)
7175 DEFSYM (Qclone_of, "clone-of"); 7253 DEFSYM (Qclone_of, "clone-of");
7176 DEFSYM (Qfloor, "floor"); 7254 DEFSYM (Qfloor, "floor");
7177 DEFSYM (Qceiling, "ceiling"); 7255 DEFSYM (Qceiling, "ceiling");
7256 DEFSYM (Qwindow_start_group_function, "window-start-group-function");
7257 DEFSYM (Qwindow_end_group_function, "window-end-group-function");
7258 DEFSYM (Qset_window_start_group_function, "set-window-start-group-function");
7259 DEFSYM (Qrecenter_group_function, "recenter-group-function");
7260 DEFSYM (Qpos_visible_in_window_p_group_function, "pos-visible-in-window-p-group-function");
7261 DEFSYM (Qmove_to_window_line_group_function, "move-to-window-line-group-function");
7178 7262
7179 staticpro (&Vwindow_list); 7263 staticpro (&Vwindow_list);
7180 7264
@@ -7346,6 +7430,72 @@ Note that this optimization can cause the portion of the buffer
7346displayed after a scrolling operation to be somewhat inaccurate. */); 7430displayed after a scrolling operation to be somewhat inaccurate. */);
7347 Vfast_but_imprecise_scrolling = false; 7431 Vfast_but_imprecise_scrolling = false;
7348 7432
7433 DEFVAR_LISP ("window-start-group-function", Vwindow_start_group_function,
7434 doc: /* Function to call for `window-start' when its GROUP parameter is non-nil.
7435When this variable contains a function, and `window-start' is called with a
7436non-nil GROUP parameter, the function is called instead of `window-start''s
7437normal action. `window-start' passes the function its argument WINDOW, which
7438might be nil. The function may recursively call `window-start'. */);
7439 Vwindow_start_group_function = Qnil;
7440 Fmake_variable_buffer_local (Qwindow_start_group_function);
7441 Fput (Qwindow_start_group_function, Qpermanent_local, Qt);
7442
7443 DEFVAR_LISP ("window-end-group-function", Vwindow_end_group_function,
7444 doc: /* Function to call for `window-end' if its GROUP parameter is non-nil.
7445When this variable contains a function, and `window-end' is called with a
7446non-nil GROUP parameter, the function is called instead of `window-end''s
7447normal action. `window-end' passes the function its two parameters WINDOW,
7448and UPDATE. The function may call `window-end' recursively. */);
7449 Vwindow_end_group_function = Qnil;
7450 Fmake_variable_buffer_local (Qwindow_end_group_function);
7451 Fput (Qwindow_end_group_function, Qpermanent_local, Qt);
7452
7453 DEFVAR_LISP ("set-window-start-group-function",
7454 Vset_window_start_group_function,
7455 doc: /* The function to call for `set-window-start' when its GROUP parameter is non-nil.
7456When this variable contains a function, and `set-window-start' is called
7457with a non-nil GROUP parameter, the function is called instead of
7458`set-window-start''s normal action. `set-window-start' passes the function
7459its three parameters WINDOW, POS, and NOFORCE. The function may call
7460`set-window-start' recursively. */);
7461 Vset_window_start_group_function = Qnil;
7462 Fmake_variable_buffer_local (Qset_window_start_group_function);
7463 Fput (Qset_window_start_group_function, Qpermanent_local, Qt);
7464
7465 DEFVAR_LISP ("recenter-group-function", Vrecenter_group_function,
7466 doc: /* Function to call for `recenter' when its GROUP parameter is non-nil.
7467When this variable contains a function, and `recenter' is called with a
7468non-nil GROUP parameter, the function is called instead of `recenter''s
7469normal action. `recenter' passes the function its parameter ARG. The
7470function may call `recenter' recursively. */);
7471 Vrecenter_group_function = Qnil;
7472 Fmake_variable_buffer_local (Qrecenter_group_function);
7473 Fput (Qrecenter_group_function, Qpermanent_local, Qt);
7474
7475 DEFVAR_LISP ("pos-visible-in-window-p-group-function",
7476 Vpos_visible_in_window_p_group_function,
7477 doc: /* Function for `pos-visible-in-window-p' when its GROUP parameter is non-nil.
7478When this variable contains a function, and `pos-visible-in-window-p' is
7479called with a non-nil GROUP parameter, the function is called instead of
7480`pos-visible-in-window-p''s normal action. `pos-visible-in-window-p'
7481passes the function its three parameters POS, WINDOW, and PARTIALLY. The
7482function may call `pos-visible-in-window-p' recursively. */);
7483 Vpos_visible_in_window_p_group_function = Qnil;
7484 Fmake_variable_buffer_local (Qpos_visible_in_window_p_group_function);
7485 Fput (Qpos_visible_in_window_p_group_function, Qpermanent_local, Qt);
7486
7487 DEFVAR_LISP ("move-to-window-line-group-function",
7488 Vmove_to_window_line_group_function,
7489 doc: /* Function for `move-to-window-line' when its GROUP parameter is non-nil.
7490When this variable contains a function, and `move-to-window-line' is
7491called with a non-nil GROUP parameter, the function is called instead of
7492`move-to-window-line''s normal action. `move-to-window-line' passes the
7493function its parameter ARG. The function may call `move-to-window-line'
7494recursively. */);
7495 Vmove_to_window_line_group_function = Qnil;
7496 Fmake_variable_buffer_local (Qmove_to_window_line_group_function);
7497 Fput (Qmove_to_window_line_group_function, Qpermanent_local, Qt);
7498
7349 defsubr (&Sselected_window); 7499 defsubr (&Sselected_window);
7350 defsubr (&Sminibuffer_window); 7500 defsubr (&Sminibuffer_window);
7351 defsubr (&Swindow_minibuffer_p); 7501 defsubr (&Swindow_minibuffer_p);