Add copy-mode -c to initialize the preferred cursor column

This commit is contained in:
Michael Grant
2026-10-05 12:29:17 +01:00
parent 8f25579c5a
commit a1825146ee
5 changed files with 206 additions and 3 deletions

View File

@@ -1,5 +1,8 @@
CHANGES FROM 3.7c TO 3.8
* Add copy-mode -c to start with the current cursor column as the preferred
column, rather than initially following line ends.
* Many improvements to floating panes:
- new-pane and split-window can now set the title with -T and border lines

View File

@@ -30,8 +30,8 @@ const struct cmd_entry cmd_copy_mode_entry = {
.name = "copy-mode",
.alias = NULL,
.args = { "dekHMqSs:t:u", 0, 0, NULL },
.usage = "[-dekHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.args = { "cdekHMqSs:t:u", 0, 0, NULL },
.usage = "[-cdekHMqSu] [-s src-pane] " CMD_TARGET_PANE_USAGE,
.source = { 's', CMD_FIND_PANE, 0 },
.target = { 't', CMD_FIND_PANE, 0 },

View File

@@ -0,0 +1,190 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
LC_ALL=C.UTF-8
export PATH TERM LC_ALL
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
TMUX="$TEST_TMUX -Lentry-column-$$ -f/dev/null"
fail()
{
echo "$mode: $*" >&2
exit 1
}
cleanup()
{
$TMUX kill-server 2>/dev/null
}
trap cleanup 0 1 15
x()
{
$TMUX send-keys -X "$@" || fail "copy command failed: $*"
}
check_cursor()
{
actual=$($TMUX display-message -p '#{copy_cursor_x},#{copy_cursor_y}')
[ "$actual" = "$1" ] || fail "expected cursor $1, got $actual"
}
wait_cursor()
{
i=0
while [ "$i" -lt 50 ]; do
actual=$($TMUX display-message -p '#{cursor_x},#{cursor_y}')
[ "$actual" = "$1" ] && return 0
sleep 0.1
i=$((i + 1))
done
fail "pane cursor did not reach $1"
}
mode=emacs
$TMUX new-session -d -x40 -y10 \
"printf '%s\r\n' ABCDEFGHIJKLMNOPQRST abc ABCDEFGHIJKLMNOPQRST \
abcdefgh ABCDEFGHIJKLMNOP ab 01234567890123456789; \
printf '\033[4;9H'; exec cat" ||
fail "new-session failed"
$TMUX set-option -g status off || fail "set status failed"
$TMUX set-option -g window-size manual || fail "set window-size failed"
wait_cursor 8,3
for mode in emacs vi; do
$TMUX set-window-option -g mode-keys "$mode" ||
fail "set mode-keys failed"
# Ordinary entry still follows line ends; -c starts at the same physical
# position but treats its column as a numeric preference.
for direction in up down; do
if [ "$direction" = up ]; then
row=2
end=20
else
row=4
end=16
fi
[ "$mode" = emacs ] || end=$((end - 1))
$TMUX copy-mode || fail "copy-mode failed"
check_cursor 8,3
x "cursor-$direction"
check_cursor "$end,$row"
x cancel
$TMUX copy-mode -c || fail "copy-mode -c failed"
check_cursor 8,3
x "cursor-$direction"
check_cursor "8,$row"
x cancel
done
# Short lines retain the existing emacs and vi movement rules. Emacs
# restores the preferred column; vi resumes from its last-character clamp.
$TMUX copy-mode -c || fail "copy-mode -c failed"
x cursor-up
check_cursor 8,2
x cursor-up
if [ "$mode" = emacs ]; then
check_cursor 3,1
restored=8
else
check_cursor 2,1
restored=2
fi
x cursor-up
check_cursor "$restored,0"
x cancel
$TMUX copy-mode -c || fail "copy-mode -c failed"
x cursor-down
check_cursor 8,4
x cursor-down
if [ "$mode" = emacs ]; then
check_cursor 2,5
restored=8
else
check_cursor 1,5
restored=1
fi
x cursor-down
check_cursor "$restored,6"
x cancel
# Horizontal movement before the first vertical move replaces the initial
# preference, rather than leaving the entry column permanently fixed.
$TMUX copy-mode -c || fail "copy-mode -c failed"
x cursor-left
check_cursor 7,3
x cursor-up
check_cursor 7,2
x cancel
done
# Passing -c while already in copy mode must not reinitialize its preference.
mode=emacs
$TMUX set-window-option -g mode-keys "$mode" || fail "set mode-keys failed"
$TMUX copy-mode -c || fail "copy-mode -c failed"
x cursor-left
x cursor-up
x cursor-up
check_cursor 3,1
$TMUX copy-mode -c || fail "second copy-mode -c failed"
check_cursor 3,1
x cursor-up
check_cursor 7,0
x cancel
# An empty entry line has a numeric column zero with -c.
$TMUX respawn-pane -k \
"printf '\033[H\033[2JABCDEFGHIJKLMNOPQRST\033[2;1H'; exec cat" ||
fail "respawn-pane failed"
wait_cursor 0,1
for mode in emacs vi; do
$TMUX set-window-option -g mode-keys "$mode" ||
fail "set mode-keys failed"
$TMUX copy-mode -c || fail "copy-mode -c failed"
check_cursor 0,1
x cursor-up
check_cursor 0,0
x cancel
done
# The initial preference counts terminal columns, not characters.
$TMUX respawn-pane -k \
"printf '\033[H\033[2J'; \
printf '%s\r\n' ABCDEFGHIJKLMNOPQRST ABCDEFGHIJKLMNOPQRST; \
printf 'abc中def'; exec cat" ||
fail "respawn-pane failed"
wait_cursor 8,2
for mode in emacs vi; do
$TMUX set-window-option -g mode-keys "$mode" ||
fail "set mode-keys failed"
$TMUX copy-mode -c || fail "copy-mode -c failed"
check_cursor 8,2
x cursor-up
check_cursor 8,1
x cancel
done
# Initialize before the optional entry-time page movement.
$TMUX respawn-pane -k \
"printf '\033[H\033[2JABCDEFGHIJKLMNOPQRST'; \
printf '\033[9;1Habcdefgh\033[10;1HABCDEFGHIJKLMNOP\033[9;9H'; \
exec cat" ||
fail "respawn-pane failed"
wait_cursor 8,8
for mode in emacs vi; do
$TMUX set-window-option -g mode-keys "$mode" ||
fail "set mode-keys failed"
$TMUX copy-mode -cu || fail "copy-mode -cu failed"
check_cursor 8,0
x cancel
$TMUX copy-mode -cd || fail "copy-mode -cd failed"
check_cursor 8,9
x cancel
done
exit 0

7
tmux.1
View File

@@ -2609,12 +2609,17 @@ The synopsis for the
command is:
.Bl -tag -width Ds
.It Xo Ic copy\-mode
.Op Fl dekHMqSu
.Op Fl cdekHMqSu
.Op Fl s Ar src\-pane
.Op Fl t Ar target\-pane
.Xc
Enter copy mode.
.Pp
.Fl c
initializes the preferred cursor column to the column at which copy mode is
entered, rather than initially following the end of each line.
Subsequent cursor movements use the normal copy mode behaviour.
.Pp
.Fl u
enters copy mode and scrolls one page up and
.Fl d

View File

@@ -614,6 +614,11 @@ window_copy_init(struct window_mode_entry *wme,
window_copy_sync_snapshot(data, base->grid);
data->cx = cx;
if (args_has(args, 'c')) {
/* Start with a preferred column rather than end of line. */
data->lastcx = cx;
data->lastsx = cx + 1;
}
if (cy < screen_hsize(data->backing)) {
data->cy = 0;
data->oy = screen_hsize(data->backing) - cy;