Function: recenter
recenter is an interactive function defined in window.c.
Signature
(recenter &optional ARG REDISPLAY)
Documentation
Center point in selected window and maybe redisplay frame.
With a numeric prefix argument ARG, recenter putting point on screen line ARG relative to the selected window. If ARG is negative, it counts up from the bottom of the window. (ARG should be less than the height of the window.)
If ARG is omitted or nil, then recenter with point on the middle line
of the selected window; if REDISPLAY & recenter-redisplay are
non-nil, also erase the entire frame and redraw it (when
auto-resize-tool-bars is set to grow-only, this resets the
tool-bar's height to the minimum height needed); if
recenter-redisplay has the special value tty, then only tty frames
are redrawn. Interactively, REDISPLAY is always non-nil.
Just C-u as prefix means put point in the center of the window and redisplay normally--don't erase and redraw the frame.
Probably introduced at or before Emacs version 27.1.
Key Bindings
Source Code
// Defined in /usr/src/emacs/src/window.c
// Skipping highlighting due to helpful-max-highlight.
{
struct window *w = XWINDOW (selected_window);
struct buffer *buf = XBUFFER (w->contents);
bool center_p = false;
ptrdiff_t charpos, bytepos;
EMACS_INT iarg UNINIT;
int this_scroll_margin;
/* For reasons why we signal an error here, see
https://lists.gnu.org/r/emacs-devel/2014-06/msg00053.html,
https://lists.gnu.org/r/emacs-devel/2014-06/msg00094.html. */
if (buf != current_buffer)
error ("`recenter'ing a window that does not display current-buffer.");
/* If redisplay is suppressed due to an error, try again. */
buf->display_error_modiff = 0;
if (NILP (arg))
{
if (!NILP (redisplay)
&& !NILP (Vrecenter_redisplay)
&& (!EQ (Vrecenter_redisplay, Qtty)
|| !NILP (Ftty_type (selected_frame))))
{
ptrdiff_t i;
/* Invalidate pixel data calculated for all compositions. */
for (i = 0; i < n_compositions; i++)
composition_table[i]->font = NULL;
#if defined (HAVE_WINDOW_SYSTEM)
WINDOW_XFRAME (w)->minimize_tab_bar_window_p = 1;
#endif
#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR)
WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1;
#endif
Fredraw_frame (WINDOW_FRAME (w));
SET_FRAME_GARBAGED (WINDOW_XFRAME (w));
}
center_p = true;
}
else if (CONSP (arg)) /* Just C-u. */
center_p = true;
else
{
arg = Fprefix_numeric_value (arg);
CHECK_FIXNUM (arg);
iarg = XFIXNUM (arg);
}
/* Do this after making BUF current
in case scroll_margin is buffer-local. */
this_scroll_margin = window_scroll_margin (w, MARGIN_IN_LINES);
/* Don't use the display code for initial frames, as the necessary
data structures might not be set up yet then. Also don't use it
for buffers with very long lines, as it tremdously slows down
redisplay, especially when lines are truncated. */
if (!FRAME_INITIAL_P (XFRAME (w->frame))
&& !current_buffer->long_line_optimizations_p)
{
specpdl_ref count = SPECPDL_INDEX ();
record_unwind_protect_void (unwind_display_working_on_window);
display_working_on_window_p = true;
if (center_p)
{
struct it it;
struct text_pos pt;
void *itdata = bidi_shelve_cache ();
SET_TEXT_POS (pt, PT, PT_BYTE);
start_display (&it, w, pt);
move_it_vertically_backward (&it, window_box_height (w) / 2);
charpos = IT_CHARPOS (it);
bytepos = IT_BYTEPOS (it);
bidi_unshelve_cache (itdata, false);
}
else if (iarg < 0)
{
struct it it;
struct text_pos pt;
ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg);
int extra_line_spacing;
int h = window_box_height (w);
int ht = window_internal_height (w);
void *itdata = bidi_shelve_cache ();
nlines = clip_to_bounds (this_scroll_margin + 1, nlines,
ht - this_scroll_margin);
SET_TEXT_POS (pt, PT, PT_BYTE);
start_display (&it, w, pt);
/* Be sure we have the exact height of the full line containing PT. */
move_it_by_lines (&it, 0);
/* The amount of pixels we have to move back is the window
height minus what's displayed in the line containing PT,
and the lines below. */
it.current_y = 0;
it.vpos = 0;
move_it_by_lines (&it, nlines);
if (it.vpos == nlines)
h -= it.current_y;
else
{
/* Last line has no newline. */
h -= line_bottom_y (&it);
it.vpos++;
}
/* Don't reserve space for extra line spacing of last line. */
extra_line_spacing = it.max_extra_line_spacing;
/* If we can't move down NLINES lines because we hit
the end of the buffer, count in some empty lines. */
if (it.vpos < nlines)
{
nlines -= it.vpos;
extra_line_spacing = it.extra_line_spacing;
h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing);
}
if (h <= 0)
{
bidi_unshelve_cache (itdata, false);
unbind_to (count, Qnil);
return Qnil;
}
/* Now find the new top line (starting position) of the window. */
start_display (&it, w, pt);
it.current_y = 0;
move_it_vertically_backward (&it, h);
/* If extra line spacing is present, we may move too far
back. This causes the last line to be only partially
visible (which triggers redisplay to recenter that line
in the middle), so move forward.
But ignore extra line spacing on last line, as it is not
considered to be part of the visible height of the line.
*/
h += extra_line_spacing;
while (-it.current_y > h && it.what != IT_EOB)
move_it_by_lines (&it, 1);
charpos = IT_CHARPOS (it);
bytepos = IT_BYTEPOS (it);
bidi_unshelve_cache (itdata, false);
}
else
{
struct it it;
struct text_pos pt;
ptrdiff_t nlines = min (PTRDIFF_MAX, iarg);
int ht = window_internal_height (w);
void *itdata = bidi_shelve_cache ();
nlines = clip_to_bounds (this_scroll_margin, nlines,
ht - this_scroll_margin - 1);
SET_TEXT_POS (pt, PT, PT_BYTE);
start_display (&it, w, pt);
/* Move to the beginning of screen line containing PT. */
move_it_by_lines (&it, 0);
/* Move back to find the point which is ARG screen lines above PT. */
if (nlines > 0)
{
it.current_y = 0;
it.vpos = 0;
move_it_by_lines (&it, -nlines);
}
charpos = IT_CHARPOS (it);
bytepos = IT_BYTEPOS (it);
bidi_unshelve_cache (itdata, false);
}
unbind_to (count, Qnil);
}
else
{
struct position pos;
int ht = window_internal_height (w);
if (center_p)
iarg = ht / 2;
else if (iarg < 0)
iarg += ht;
/* Don't let it get into the margin at either top or bottom. */
iarg = clip_to_bounds (this_scroll_margin, iarg,
ht - this_scroll_margin - 1);
pos = *vmotion (PT, PT_BYTE, - iarg, w);
charpos = pos.bufpos;
bytepos = pos.bytepos;
}
/* Set the new window start. */
set_marker_both (w->start, w->contents, charpos, bytepos);
/* The window start was calculated with an iterator already adjusted
by the existing vscroll, so w->start must not be combined with
retaining the existing vscroll, which redisplay will not reset if
w->preserve_vscroll_p is enabled. (bug#70386) */
w->vscroll = 0;
w->preserve_vscroll_p = false;
w->window_end_valid = false;
w->optional_new_start = true;
w->start_at_line_beg = (bytepos == BEGV_BYTE
|| FETCH_BYTE (bytepos - 1) == '\n');
wset_redisplay (w);
return Qnil;
}