From 543032af24b0fd77cee90a309e24a482efd0624f Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 7 May 2026 09:15:44 +0000 Subject: [PATCH] Add Emacs-style recentre-top-bottom, GitHub issue 5053 from sinyax75 at gmail dot com. --- usr.bin/tmux/key-bindings.c | 4 +- usr.bin/tmux/tmux.1 | 9 ++++- usr.bin/tmux/window-copy.c | 76 ++++++++++++++++++++++++++++++++++++- 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/usr.bin/tmux/key-bindings.c b/usr.bin/tmux/key-bindings.c index b28f7790566..254f2dbe62e 100644 --- a/usr.bin/tmux/key-bindings.c +++ b/usr.bin/tmux/key-bindings.c @@ -1,4 +1,4 @@ -/* $OpenBSD: key-bindings.c,v 1.167 2026/04/28 10:01:07 nicm Exp $ */ +/* $OpenBSD: key-bindings.c,v 1.168 2026/05/07 09:15:44 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -503,7 +503,7 @@ key_bindings_init(void) "bind -Tcopy-mode C-b { send -X cursor-left }", "bind -Tcopy-mode C-g { send -X clear-selection }", "bind -Tcopy-mode C-k { send -X copy-pipe-end-of-line-and-cancel }", - "bind -Tcopy-mode C-l { send -X cursor-centre-vertical }", + "bind -Tcopy-mode C-l { send -X recentre-top-bottom }", "bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }", "bind -Tcopy-mode C-n { send -X cursor-down }", "bind -Tcopy-mode C-p { send -X cursor-up }", diff --git a/usr.bin/tmux/tmux.1 b/usr.bin/tmux/tmux.1 index 0c73b105783..d3ce584d1a3 100644 --- a/usr.bin/tmux/tmux.1 +++ b/usr.bin/tmux/tmux.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: tmux.1,v 1.1052 2026/05/03 15:02:48 nicm Exp $ +.\" $OpenBSD: tmux.1,v 1.1053 2026/05/07 09:15:44 nicm Exp $ .\" .\" Copyright (c) 2007 Nicholas Marriott .\" @@ -14,7 +14,7 @@ .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: May 3 2026 $ +.Dd $Mdocdate: May 7 2026 $ .Dt TMUX 1 .Os .Sh NAME @@ -2222,6 +2222,11 @@ Turn off rectangle selection mode. .Xc Toggle rectangle selection mode. .It Xo +.Ic recentre\-top\-bottom +(emacs : C-l) +.Xc +Cycles the current line between centre, top, and bottom. +.It Xo .Ic refresh\-from\-pane (vi: r) (emacs: r) diff --git a/usr.bin/tmux/window-copy.c b/usr.bin/tmux/window-copy.c index 132c80156e7..1f2b89dca56 100644 --- a/usr.bin/tmux/window-copy.c +++ b/usr.bin/tmux/window-copy.c @@ -1,4 +1,4 @@ -/* $OpenBSD: window-copy.c,v 1.396 2026/05/01 09:44:42 nicm Exp $ */ +/* $OpenBSD: window-copy.c,v 1.397 2026/05/07 09:15:44 nicm Exp $ */ /* * Copyright (c) 2007 Nicholas Marriott @@ -286,6 +286,13 @@ struct window_copy_mode_data { SEL_LINE, /* select one line at a time */ } selflag; + enum { + RECENTRE_TOP, + RECENTRE_MIDDLE, + RECENTRE_BOTTOM, + } recentre_state; + u_int recentre_line; + const char *separators; /* word separators */ u_int dx; /* drag start position */ @@ -462,7 +469,7 @@ window_copy_init(struct window_mode_entry *wme, struct screen *base = &wp->base; struct screen_write_ctx ctx; u_int i, cx, cy; - + data = window_copy_common_init(wme); data->backing = window_copy_clone_screen(base, &data->screen, &cx, &cy, wme->swp != wme->wp); @@ -497,6 +504,9 @@ window_copy_init(struct window_mode_entry *wme, screen_size_x(&data->screen)), data->cy, 0); screen_write_stop(&ctx); + data->recentre_state = RECENTRE_MIDDLE; + data->recentre_line = 0; + return (&data->screen); } @@ -2789,6 +2799,62 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_REDRAW); } +static enum window_copy_cmd_action +window_copy_cmd_recentre_top_bottom(struct window_copy_cmd_state *cs) +{ + struct window_mode_entry *wme = cs->wme; + struct window_copy_mode_data *data = wme->data; + u_int cy = data->cy, oy = data->oy; + u_int sy = screen_size_y(&data->screen) - 1; + u_int sm = sy / 2, backing_row; + enum { MIDDLE, TOP, BOTTOM } target; + + backing_row = screen_hsize(data->backing) + cy - data->oy; + if (data->recentre_line != backing_row) { + data->recentre_state = RECENTRE_MIDDLE; + data->recentre_line = backing_row; + } + + switch (data->recentre_state) { + case RECENTRE_MIDDLE: + data->recentre_state = RECENTRE_TOP; + target = MIDDLE; + break; + case RECENTRE_TOP: + data->recentre_state = RECENTRE_BOTTOM; + target = TOP; + break; + case RECENTRE_BOTTOM: + default: + data->recentre_state = RECENTRE_MIDDLE; + target = BOTTOM; + break; + } + + oy = data->oy; + switch (target) { + case MIDDLE: + if (cy < sm) + window_copy_scroll_down(wme, sm - cy); + else if (cy > sm) + window_copy_scroll_up(wme, cy - sm); + if (data->oy != oy) + data->cy = cy + (data->oy - oy); + break; + case TOP: + window_copy_scroll_up(wme, cy); + data->cy = cy - (oy - data->oy); + break; + case BOTTOM: + window_copy_scroll_down(wme, sy - cy); + data->cy = cy + (data->oy - oy); + break; + } + window_copy_update_selection(wme, 0, 0); + + return (WINDOW_COPY_CMD_REDRAW); +} + static const struct { const char *command; u_int minargs; @@ -3173,6 +3239,12 @@ static const struct { .clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY, .f = window_copy_cmd_previous_word }, + { .command = "recentre-top-bottom", + .args = { "", 0, 0, NULL }, + .flags = WINDOW_COPY_CMD_FLAG_READONLY, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_recentre_top_bottom + }, { .command = "rectangle-on", .args = { "", 0, 0, NULL }, .flags = 0,