Merge branch 'master' into command_parser

# Conflicts:
#	arguments.c
#	cmd-command-prompt.c
#	cmd-parse.y
#	cmd-run-shell.c
#	window-panes.c
This commit is contained in:
Nicholas Marriott
2026-09-01 20:17:10 +01:00
47 changed files with 1212 additions and 193 deletions

View File

@@ -28,7 +28,7 @@ $TMUX kill-server 2>/dev/null
TMP=$(mktemp)
TMP2=$(mktemp)
trap 'rm -f "$TMP" "$TMP2"; $TMUX kill-server 2>/dev/null' 0 1 15
trap 'rm -f "$TMP" "$TMP2" "$TMP".*; $TMUX kill-server 2>/dev/null' 0 1 15
# check_ok $cmd...
#
@@ -270,6 +270,27 @@ if [ $? -eq 0 ]; then
exit 1
fi
# A write error after a successful open must be reported to the server.
dd if=/dev/zero of="$TMP.big" bs=100000 count=1 2>/dev/null
check_ok load-buffer -b write-error "$TMP.big"
mkfifo "$TMP.fifo" || exit 1
dd if="$TMP.fifo" of="$TMP.got" bs=1 count=8 2>/dev/null &
reader=$!
exec 3>"$TMP.fifo"
out=$($TMUX save-buffer -b write-error "$TMP.fifo" 3>&- 2>&1)
status=$?
exec 3>&-
wait "$reader"
if [ $status -eq 0 ]; then
echo "save-buffer after write error succeeded."
exit 1
fi
if [ "$(wc -c <"$TMP.got")" -ne 8 ]; then
echo "save-buffer write error test wrote wrong amount."
exit 1
fi
check_ok delete-buffer -b write-error
# save-buffer - writes to stdout and load-buffer - reads from stdin.
out=$($TMUX save-buffer -b sb -)
if [ "$out" != "data" ]; then

View File

@@ -0,0 +1,163 @@
#!/bin/sh
PATH=/bin:/usr/bin
TERM=screen
[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux)
fail()
{
echo "$1"
[ -z "$TMUX" ] || $TMUX kill-server 2>/dev/null
exit 1
}
goto_cell()
{
row=$1
column=$2
$TMUX send-keys -X history-top || fail "$mode: history-top failed"
$TMUX send-keys -X start-of-line || fail "$mode: start-of-line failed"
[ "$row" -eq 0 ] ||
$TMUX send-keys -X -N "$row" cursor-down ||
fail "$mode: cursor-down failed"
[ "$column" -eq 0 ] ||
$TMUX send-keys -X -N "$column" cursor-right ||
fail "$mode: cursor-right failed"
}
check_cursor()
{
expected=$1
actual=$($TMUX display-message -p '#{copy_cursor_x},#{copy_cursor_y}')
[ "$actual" = "$expected" ] ||
fail "$mode: expected cursor $expected, got $actual"
}
check_word()
{
row=$1
column=$2
expected=$3
goto_cell "$row" "$column"
$TMUX set-buffer sentinel || fail "$mode: set-buffer failed"
$TMUX send-keys -X select-word || fail "$mode: select-word failed"
$TMUX send-keys -X copy-selection || fail "$mode: copy-selection failed"
actual=$($TMUX show-buffer 2>/dev/null)
[ "$actual" = "$expected" ] ||
fail "$mode: expected word '$expected', got '$actual'"
}
check_next_word()
{
row=$1
expected=$2
goto_cell "$row" 0
$TMUX send-keys -X next-word || fail "$mode: next-word failed"
$TMUX set-buffer sentinel || fail "$mode: set-buffer failed"
$TMUX send-keys -X select-word || fail "$mode: select-word failed"
$TMUX send-keys -X copy-selection || fail "$mode: copy-selection failed"
actual=$($TMUX show-buffer 2>/dev/null)
[ "$actual" = "$expected" ] ||
fail "$mode: expected next word '$expected', got '$actual'"
}
for mode in emacs vi; do
TMUX="$TEST_TMUX -Lunicode-whitespace-$mode-$$ -f/dev/null"
$TMUX kill-server 2>/dev/null
$TMUX new-session -d -x16 -y20 \
"printf 'aa bb\naa\tbb\naa\302\240bb\naa\343\200\200bb\naa:bb\naaaaaaaaaaaaaaa\302\240b\nabcdefghijklmnopq\nhardone\nhardtwo\naa\341\232\200bb\naa\342\200\200bb\naa\342\200\250bb\naa\342\200\251bb\naa\342\200\257bb\naa\342\201\237bb\nxa\302\205b c\n'; exec cat" ||
fail "$mode: new-session failed"
$TMUX set-option -g window-size manual || fail "$mode: set size failed"
$TMUX set-window-option -g mode-keys "$mode" ||
fail "$mode: set mode-keys failed"
$TMUX set-window-option -g word-separators "" ||
fail "$mode: clear word-separators failed"
$TMUX copy-mode || fail "$mode: copy-mode failed"
# ASCII space and TAB retain their existing behaviour.
goto_cell 0 0
$TMUX send-keys -X next-space || fail "$mode: ASCII next-space failed"
check_cursor 3,0
goto_cell 1 0
$TMUX send-keys -X next-space || fail "$mode: TAB next-space failed"
check_cursor 8,1
$TMUX send-keys -X previous-space ||
fail "$mode: TAB previous-space failed"
check_cursor 0,1
# NBSP is whitespace for selection and word movement.
goto_cell 2 0
$TMUX send-keys -X next-word || fail "$mode: NBSP next-word failed"
check_cursor 3,2
$TMUX send-keys -X previous-word ||
fail "$mode: NBSP previous-word failed"
check_cursor 0,2
check_word 2 0 aa
goto_cell 2 0
$TMUX send-keys -X next-word-end ||
fail "$mode: NBSP next-word-end failed"
if [ "$mode" = emacs ]; then
check_cursor 2,2
else
check_cursor 1,2
fi
# U+3000 is two columns wide; both its leading and padding cells must be
# skipped as one whitespace character in either direction.
goto_cell 3 0
$TMUX send-keys -X next-word || fail "$mode: U+3000 next-word failed"
check_cursor 4,3
$TMUX send-keys -X previous-word ||
fail "$mode: U+3000 previous-word failed"
check_cursor 0,3
goto_cell 3 0
$TMUX send-keys -X next-space-end ||
fail "$mode: U+3000 next-space-end failed"
if [ "$mode" = emacs ]; then
check_cursor 2,3
else
check_cursor 1,3
fi
# Other characters in word-separators remain literal when space enables
# Unicode whitespace matching.
$TMUX set-window-option -g word-separators ': ' ||
fail "$mode: set custom word-separators failed"
goto_cell 4 0
$TMUX send-keys -X next-word || fail "$mode: literal next-word failed"
check_cursor 2,4
$TMUX set-window-option -g word-separators "" ||
fail "$mode: restore word-separators failed"
# Whitespace in the last column remains a boundary across a soft wrap.
goto_cell 5 0
$TMUX send-keys -X next-space ||
fail "$mode: wrapped NBSP next-space failed"
check_cursor 0,6
# A word split only by a soft wrap remains one word, while a hard line
# boundary still terminates selection.
check_word 7 0 abcdefghijklmnopq
check_word 9 0 hardone
# Check the remaining representative White_Space ranges and U+0085
# combined into a cell with another character.
check_next_word 11 bb
check_next_word 12 bb
check_next_word 13 bb
check_next_word 14 bb
check_next_word 15 bb
check_next_word 16 bb
goto_cell 17 0
$TMUX send-keys -X next-word ||
fail "$mode: combined U+0085 next-word failed"
check_cursor 2,17
$TMUX kill-server 2>/dev/null
done
exit 0

View File

@@ -199,5 +199,74 @@ must_fail $TMUX resize-pane -t "$id" -x 0
must_fail $TMUX resize-pane -t "$id" -y 0
$TMUX kill-pane -t "$id" || exit 1
# --- Tiled pane resize with floating cells in the layout ---
$TMUX set-option -w -u pane-border-lines || exit 1
base=$($TMUX display-message -p '#{pane_id}')
# A floating cell after the last tiled cell must not become the recipient of a
# relative or absolute resize. This is the sequence from GitHub issue 5135.
floating=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
-x 50% -y 50% -X 50% -Y 50% 'sleep 100') ||
fail "new-pane for tiled resize test failed"
lower=$($TMUX split-window -dPF '#{pane_id}' -t "$base" 'sleep 100') ||
fail "split-window for tiled resize test failed"
$TMUX resize-pane -t "$lower" -U 5 || fail "relative tiled resize failed"
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 7
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 8
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 16
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_top}')" 13
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 10
$TMUX resize-pane -t "$lower" -y 11 || fail "absolute tiled reset failed"
$TMUX resize-pane -t "$lower" -y 16 || fail "absolute tiled resize failed"
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 7
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 8
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 16
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 10
$TMUX kill-pane -t "$floating" || exit 1
$TMUX kill-pane -t "$lower" || exit 1
# A floating cell between tiled siblings must be skipped when finding the cell
# which donates space to a resize.
lower=$($TMUX split-window -dPF '#{pane_id}' -t "$base" 'sleep 100') ||
fail "split-window for middle floating cell test failed"
floating=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
-x 20 -y 8 -X 30 -Y 8 'sleep 100') ||
fail "new-pane for middle floating cell test failed"
$TMUX resize-pane -t "$base" -D 3 || fail "resize past floating cell failed"
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 15
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 16
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 8
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 6
$TMUX kill-pane -t "$floating" || exit 1
$TMUX kill-pane -t "$lower" || exit 1
# A small floating child must not limit the available tiled space in a nested
# layout with a different split direction.
lower=$($TMUX split-window -dPF '#{pane_id}' -t "$base" 'sleep 100') ||
fail "split-window for nested floating cell test failed"
right=$($TMUX split-window -dhPF '#{pane_id}' -t "$lower" 'sleep 100') ||
fail "horizontal split for nested floating cell test failed"
floating=$($TMUX new-pane -dPF '#{pane_id}' -t "$lower" \
-x 20 -y 3 -X 30 -Y 10 'sleep 100') ||
fail "new-pane for nested floating cell test failed"
$TMUX resize-pane -t "$base" -D 3 || fail "nested tiled resize failed"
must_equal "$($TMUX display-message -p -t "$base" '#{pane_height}')" 15
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_top}')" 16
must_equal "$($TMUX display-message -p -t "$lower" '#{pane_height}')" 8
must_equal "$($TMUX display-message -p -t "$right" '#{pane_top}')" 16
must_equal "$($TMUX display-message -p -t "$right" '#{pane_height}')" 8
must_equal "$($TMUX display-message -p -t "$floating" '#{pane_height}')" 1
$TMUX kill-pane -t "$floating" || exit 1
$TMUX kill-pane -t "$right" || exit 1
$TMUX kill-pane -t "$lower" || exit 1
$TMUX kill-server 2>/dev/null
exit 0

View File

@@ -145,12 +145,16 @@ case "$(fmt "$modal" '#{pane_flags}')" in
*O*) ;;
*) fail "modal pane flags do not include O" ;;
esac
case "$(fmt "$modal" '#{pane_flags}')" in
*A*) ;;
*) fail "modal pane flags do not include A" ;;
esac
case "$(fmt modal:0 '#{window_flags}')" in
*O*) ;;
*) fail "modal window flags do not include O" ;;
esac
must_equal "$(fmt modal:0 '#{window_modal_pane}')" "$modal"
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 0
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
check_fail "window already has a modal pane" \
new-pane -O -x 10 -y 4 'cat'
@@ -177,12 +181,14 @@ under=$($TMUX split-window -PF '#{pane_id}' -t "$p0" 'cat') ||
sleep 1
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
must_equal "$(fmt "$under" '#{pane_active}')" 0
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
float=$($TMUX new-pane -PF '#{pane_id}' -x 10 -y 4 -X 5 -Y 3 'cat') ||
fail "new floating pane under modal failed"
sleep 1
must_equal "$(fmt modal:0 '#{pane_id}')" "$modal"
must_equal "$(fmt "$float" '#{pane_active}')" 0
must_equal "$(fmt modal:0 '#{window_zoomed_flag}')" 1
check_ok new-window -d -t modal: -n other 'cat'
check_ok select-window -t modal:other
@@ -321,6 +327,7 @@ check_ok kill-pane -t "$modal"
sleep 1
must_equal "$(fmt modal:0 '#{window_modal_pane}')" ''
must_equal "$(fmt modal:0 '#{pane_id}')" "$p0"
must_equal "$(fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 0:0
ignored=$($TMUX new-pane -KdPF '#{pane_id}' -t "$p0" 'cat') ||
fail "new-pane -K without -O failed"
@@ -352,5 +359,132 @@ new_top=$(fmt "$modal" '#{pane_top}')
check_ok kill-pane -t "$modal"
sleep 1
# A nonmodal floating pane may remain above zoom, and switching between it and
# the zoomed tiled pane must not unzoom the window.
check_ok new-window -d -t modal: -n float-over-zoom 'cat'
base=$(fmt modal:float-over-zoom '#{pane_id}')
check_ok split-window -dh -t "$base" 'cat'
check_ok resize-pane -Z -t "$base"
over=$($TMUX new-pane -APF '#{pane_id}' -t "$base" \
-x 20 -y 5 -X 20 -Y 10 'cat') ||
fail "new-pane -A failed"
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_active}')" 1:1
case "$(fmt "$over" '#{pane_flags}')" in
*A*) ;;
*) fail "float-over-zoom pane flags do not include A" ;;
esac
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
check_ok select-pane -t "$base"
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_active}')" 1:1
check_ok select-pane -t "$over"
must_equal "$(fmt "$over" '#{window_zoomed_flag}:#{pane_active}')" 1:1
client=$($TMUX list-clients -F '#{client_name}' | head -1)
[ -n "$client" ] || fail "no client for switch-client test"
check_ok switch-client -c "$client" -t "$base"
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_active}')" 1:1
check_ok switch-client -c "$client" -t "$over"
must_equal "$(fmt "$over" '#{window_zoomed_flag}:#{pane_active}')" 1:1
check_ok kill-pane -t "$over"
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
check_ok resize-pane -Z -t "$base"
ignored=$($TMUX new-pane -ALdPF '#{pane_id}' -t "$base" 'cat') ||
fail "new-pane -A -L failed"
case "$(fmt "$ignored" '#{pane_flags}')" in
*A*) fail "tiled pane flags include A" ;;
*) ;;
esac
# Existing floating panes are filtered when zoom begins: -A panes remain in the
# visible layout and ordinary floating panes do not.
check_ok new-window -d -t modal: -n existing-over-zoom 'cat'
base=$(fmt modal:existing-over-zoom '#{pane_id}')
check_ok split-window -dh -t "$base" 'cat'
over=$($TMUX new-pane -AdPF '#{pane_id}' -t "$base" \
-x 20 -y 5 -X 20 -Y 10 'cat') ||
fail "pre-existing new-pane -A failed"
under=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
-x 15 -y 4 -X 2 -Y 2 'cat') ||
fail "pre-existing ordinary new-pane failed"
check_ok select-window -t modal:existing-over-zoom
check_ok select-pane -t "$base"
check_ok resize-pane -Z -t "$base"
must_equal "$(fmt "$over" '#{pane_floating_flag}')" 1
must_equal "$(fmt "$under" '#{pane_floating_flag}')" 0
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
# Geometry changed in the visible zoom layout is copied back when unzooming.
check_ok select-pane -t "$over"
left=$(fmt "$over" '#{pane_left}')
top=$(fmt "$over" '#{pane_top}')
meta_drag $((left + 2)) $((top + 2)) $((left + 7)) $((top + 4))
new_left=$(fmt "$over" '#{pane_left}')
new_top=$(fmt "$over" '#{pane_top}')
[ "$new_left" -gt "$left" ] || [ "$new_top" -gt "$top" ] ||
fail "float-over-zoom pane did not move"
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
check_ok resize-pane -Z -t "$base"
must_equal "$(fmt "$over" '#{pane_left}:#{pane_top}')" \
"$new_left:$new_top"
must_equal "$(fmt "$under" '#{pane_floating_flag}')" 1
# Resizing with the over-zoom pane active restores the original zoom target.
check_ok resize-pane -Z -t "$base"
check_ok select-pane -t "$over"
check_ok resize-window -t modal:existing-over-zoom -x 90 -y 30
must_equal "$(fmt "$base" \
'#{window_width}x#{window_height}:#{window_zoomed_flag}:#{pane_zoomed_flag}')" \
90x30:1:1
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_active}')" 1:1
# Natural pane exit uses a different removal path from kill-pane and must also
# preserve zoom.
dying=$($TMUX new-pane -AdPF '#{pane_id}' -t "$base" \
-x 12 -y 4 -X 4 -Y 3 'true') ||
fail "short-lived new-pane -A failed"
i=0
while $TMUX list-panes -a -F '#{pane_id}' | grep -qx "$dying"; do
i=$((i + 1))
[ $i -gt 50 ] && fail "short-lived float-over-zoom pane did not exit"
sleep 0.1
done
must_equal "$(fmt "$base" '#{window_zoomed_flag}:#{pane_zoomed_flag}')" 1:1
# A pane with -A is also above a zoom target which was itself floating. The
# temporary tiled target must sit behind retained floating panes, then return
# to the normal floating z order when unzoomed.
check_ok new-window -d -t modal: -n floating-zoom-target 'cat'
base=$(fmt modal:floating-zoom-target '#{pane_id}')
check_ok split-window -dh -t "$base" 'cat'
target=$($TMUX new-pane -dPF '#{pane_id}' -t "$base" \
-x 30 -y 10 -X 10 -Y 5 'cat') ||
fail "floating zoom target creation failed"
over=$($TMUX new-pane -AdPF '#{pane_id}' -t "$base" \
-x 15 -y 5 -X 15 -Y 8 'cat') ||
fail "float-over-zoom pane creation failed"
check_ok select-window -t modal:floating-zoom-target
check_ok select-pane -t "$target"
must_equal "$(fmt "$target" '#{pane_z}')" 0
must_equal "$(fmt "$over" '#{pane_z}')" 1
check_ok resize-pane -Z -t "$target"
must_equal "$(fmt "$target" \
'#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_floating_flag}:#{pane_z}')" \
1:1:0:2
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_z}')" 1:0
check_ok select-pane -t "$over"
must_equal "$(fmt "$over" '#{window_zoomed_flag}:#{pane_active}')" 1:1
check_ok resize-pane -Z -t "$target"
must_equal "$(fmt "$over" '#{pane_active}:#{pane_z}')" 1:0
must_equal "$(fmt "$target" '#{pane_floating_flag}:#{pane_z}')" 1:1
# If the target remains active, it returns to the front on unzoom.
check_ok select-pane -t "$target"
check_ok resize-pane -Z -t "$target"
must_equal "$(fmt "$over" '#{pane_floating_flag}:#{pane_z}')" 1:0
check_ok resize-pane -Z -t "$target"
must_equal "$(fmt "$target" '#{pane_active}:#{pane_z}')" 1:0
must_equal "$(fmt "$over" '#{pane_z}')" 1
cleanup
exit 0

View File

@@ -19,7 +19,10 @@
# - respawn-pane/respawn-window refusing a live pane without -k, working on a
# dead pane (remain-on-exit) and killing with -k;
# - resize-pane -x/-y in cells and percent, -L/-R/-U/-D adjustments and -Z
# zoom/unzoom (including implicit unzoom on split).
# zoom/unzoom, layout round trips, zoom target selection, pane removal,
# window resizing and implicit unzoom on layout changes;
# - swap-pane preserving one or two zoomed windows, including cross-window
# swaps of hidden panes and zoom targets.
#
# window-ops.sh covers window-level commands and buffers.sh paste buffers.
@@ -144,7 +147,8 @@ check_panes P:0 "0:$p3 1:$p0 2:$p2 3:$p1"
# break-pane and join-pane.
# break-pane moves a pane to a new window; -P -F prints where it went and -n
# names the new window.
# names the new window. Moving a pane out of a zoomed window unzooms it first.
check_ok resize-pane -Z -t "$p0"
out=$($TMUX break-pane -d -P -F '#{window_index}:#{pane_id}' -n broken \
-s "$p1" -t P:)
if [ "$out" != "1:$p1" ]; then
@@ -153,10 +157,14 @@ if [ "$out" != "1:$p1" ]; then
fi
check_fmt 'P:1' '#{window_name}:#{window_panes}' 'broken:1'
check_fmt 'P:0' '#{window_panes}' '3'
check_fmt 'P:0' '#{window_zoomed_flag}' '0'
# join-pane -v moves it back (the source window, left empty, is destroyed).
# join-pane -v moves it back (the source window, left empty, is destroyed) and
# also unzooms the destination before changing its layout.
check_ok resize-pane -Z -t "$p0"
check_ok join-pane -d -v -s P:broken.0 -t "$p2"
check_fmt 'P:0' '#{window_panes}' '4'
check_fmt 'P:0' '#{window_zoomed_flag}' '0'
if $TMUX has-session -t P:broken 2>/dev/null; then
echo "Window 'broken' still exists after join-pane."
exit 1
@@ -299,6 +307,122 @@ check_fmt 'P:0' '#{window_panes}' '5'
p6=$($TMUX display-message -p -t P:0.2 '#{pane_id}')
check_ok kill-pane -t "$p6"
# Zoom and unzoom preserve the exact tiled layout. Selecting another pane
# without -Z unzooms, while -Z transfers zoom to the selected pane.
layout=$($TMUX display-message -p -t P:0 '#{window_layout}')
check_ok select-pane -t "$p0"
check_ok resize-pane -Z -t "$p0"
check_ok select-pane -t "$p2"
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_active}' '0:1'
check_fmt P:0 '#{window_layout}' "$layout"
check_ok select-pane -t "$p0"
check_ok resize-pane -Z -t "$p0"
check_ok select-pane -Z -t "$p2"
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'1:1:1'
check_ok resize-pane -Z -t "$p2"
check_fmt P:0 '#{window_layout}' "$layout"
# Directional selection temporarily restores the full layout to find its
# neighbour, then follows the same unzoom or -Z transfer rules.
check_ok select-pane -t "$p0"
check_ok resize-pane -Z -t "$p0"
check_ok select-pane -t "$p0"
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'1:1:1'
check_ok select-pane -D -t "$p0"
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_active}' '0:1'
check_ok select-pane -t "$p0"
check_ok resize-pane -Z -t "$p0"
check_ok select-pane -D -Z -t "$p0"
check_fmt "$p2" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'1:1:1'
check_ok resize-pane -Z -t "$p2"
check_fmt P:0 '#{window_layout}' "$layout"
# The last-pane path has separate zoom handling, both with and without -Z.
check_ok select-pane -t "$p0"
check_ok select-pane -t "$p2"
check_ok resize-pane -Z -t "$p2"
check_ok select-pane -l -t P:0
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'0:0:1'
check_ok select-pane -t "$p0"
check_ok select-pane -t "$p2"
check_ok resize-pane -Z -t "$p2"
check_ok select-pane -l -Z -t P:0
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'1:1:1'
check_ok resize-pane -Z -t "$p0"
check_fmt P:0 '#{window_layout}' "$layout"
# Killing either a hidden ordinary pane or the zoom target unzooms.
check_ok new-window -d -t P:12 -n zoom-kill 'cat'
zk0=$($TMUX display-message -p -t P:12.0 '#{pane_id}')
zk1=$($TMUX split-window -d -P -F '#{pane_id}' -t P:12.0 'cat')
zk2=$($TMUX split-window -d -P -F '#{pane_id}' -t P:12.0 'cat')
check_ok resize-pane -Z -t "$zk0"
check_ok kill-pane -t "$zk1"
check_fmt "$zk0" '#{window_panes}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
'2:0:0'
check_ok resize-pane -Z -t "$zk0"
check_ok kill-pane -t "$zk0"
check_fmt "$zk2" '#{window_panes}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
'1:0:0'
check_ok kill-window -t P:12
# The same cases through natural process exit exercise server_destroy_pane.
check_ok new-window -d -t P:13 -n zoom-exit 'cat'
ze0=$($TMUX display-message -p -t P:13.0 '#{pane_id}')
ze1=$($TMUX split-window -d -P -F '#{pane_id}' -t P:13.0 'cat')
ze2=$($TMUX split-window -d -P -F '#{pane_id}' -t P:13.0 'cat')
check_ok resize-pane -Z -t "$ze0"
check_ok send-keys -t "$ze1" C-d
i=0
while [ "$($TMUX display-message -p -t P:13 '#{window_panes}')" != 2 ]; do
i=$((i + 1))
[ $i -gt 50 ] && { echo "Hidden pane did not exit."; exit 1; }
sleep 0.1
done
check_fmt "$ze0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '0:0'
check_ok resize-pane -Z -t "$ze2"
check_ok send-keys -t "$ze2" C-d
i=0
while [ "$($TMUX display-message -p -t P:13 '#{window_panes}')" != 1 ]; do
i=$((i + 1))
[ $i -gt 50 ] && { echo "Zoomed pane did not exit."; exit 1; }
sleep 0.1
done
check_fmt "$ze0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '0:0'
check_ok kill-window -t P:13
# resize-window restores both tiled and floating zoom targets after rebuilding
# the layout at the new size.
check_ok new-window -d -t P:14 -n zoom-resize 'cat'
zr0=$($TMUX display-message -p -t P:14.0 '#{pane_id}')
check_ok split-window -d -t P:14.0 'cat'
check_ok resize-pane -Z -t "$zr0"
check_ok resize-window -t P:14 -x 90 -y 30
check_fmt "$zr0" \
'#{window_width}x#{window_height}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
'90x30:1:1'
check_ok resize-pane -Z -t "$zr0"
zrf=$($TMUX new-pane -dP -F '#{pane_id}' -t P:14 -x 20 -y 6 'cat')
zrf_size=$($TMUX display-message -p -t "$zrf" \
'#{pane_width}x#{pane_height}')
check_ok resize-pane -Z -t "$zrf"
check_ok resize-window -t P:14 -x 100 -y 32
check_fmt "$zrf" \
'#{window_width}x#{window_height}:#{window_zoomed_flag}:#{pane_zoomed_flag}' \
'100x32:1:1'
check_ok resize-pane -Z -t "$zrf"
check_fmt "$zrf" '#{pane_floating_flag}:#{pane_width}x#{pane_height}' \
"1:$zrf_size"
check_ok kill-window -t P:14
# ---------------------------------------------------------------------------
# kill-pane.
@@ -307,8 +431,10 @@ check_ok kill-pane -t "$p3"
check_panes P:0 "0:$p0 1:$p2 2:$p1"
# -a kills every pane except the target.
check_ok resize-pane -Z -t "$p0"
check_ok kill-pane -a -t "$p0"
check_panes P:0 "0:$p0"
check_fmt "$p0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '0:0'
# Killing the last pane in a window kills the window.
check_ok new-window -d -t P:7 -n goner
@@ -516,6 +642,10 @@ check_panes P:3 "0:$r0 1:$r1 2:$r2"
# Swapping a pane with itself quietly does nothing.
check_ok swap-pane -d -s "$r1" -t "$r1"
check_panes P:3 "0:$r0 1:$r1 2:$r2"
check_ok resize-pane -Z -t "$r1"
check_ok swap-pane -d -Z -s "$r1" -t "$r1"
check_fmt "$r1" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '1:1'
check_ok resize-pane -Z -t "$r1"
# Panes can be swapped between different windows.
check_ok swap-pane -d -s "$o0" -t "$r1"
@@ -525,10 +655,38 @@ check_ok swap-pane -d -s "$r1" -t "$o0"
check_panes P:3 "0:$r0 1:$r1 2:$r2"
check_panes P:5 "0:$o0"
# With both windows zoomed, -Z preserves each window's zoom when hidden panes
# are exchanged and when the zoom targets themselves are exchanged.
o1=$($TMUX split-window -dP -F '#{pane_id}' -t P:5.0)
check_ok resize-pane -Z -t "$r0"
check_ok resize-pane -Z -t "$o0"
check_ok swap-pane -d -Z -s "$r1" -t "$o1"
check_fmt "$r0" \
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'swaps:1:1'
check_fmt "$o0" \
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'other:1:1'
check_fmt "$r1" '#{window_name}' 'other'
check_fmt "$o1" '#{window_name}' 'swaps'
check_ok swap-pane -d -Z -s "$r0" -t "$o0"
check_fmt "$o0" \
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'swaps:1:1'
check_fmt "$r0" \
'#{window_name}:#{window_zoomed_flag}:#{pane_zoomed_flag}' 'other:1:1'
check_ok resize-pane -Z -t "$o0"
check_ok resize-pane -Z -t "$r0"
# Restore the original panes before the remaining swap tests.
check_ok swap-pane -d -s "$r0" -t "$o0"
check_ok swap-pane -d -s "$r1" -t "$o1"
check_ok kill-pane -t "$o1"
check_panes P:3 "0:$r0 1:$r1 2:$r2"
check_panes P:5 "0:$o0"
# -Z keeps the window zoomed across the swap.
check_ok resize-pane -Z -t "$r0"
check_ok swap-pane -d -Z -s "$r0" -t "$r1"
check_fmt 'P:3' '#{window_zoomed_flag}' '1'
check_fmt "$r0" '#{window_zoomed_flag}:#{pane_zoomed_flag}' '1:1'
check_ok resize-pane -Z -t P:3
check_panes P:3 "0:$r1 1:$r0 2:$r2"

View File

@@ -105,6 +105,21 @@ sleep 1
CLIENT=$($TMUX2 list-clients -F '#{client_name}' | head -1)
[ -n "$CLIENT" ] || fail "no inner client"
# A variation selector which widens the previous character must use window
# coordinates when checking visibility in a pane with a nonzero x offset.
$TMUX2 set -s variation-selector-always-wide on || exit 1
WINDOW=$($TMUX2 neww -dPF '#{window_id}' "exec sleep 100") || exit 1
PANE=$($TMUX2 splitw -dhPF '#{pane_id}' -t "$WINDOW" \
"exec sleep 100") || exit 1
$TMUX2 selectw -t "$WINDOW" || exit 1
$TMUX2 respawnp -k -t "$PANE" \
"printf '\nA\342\234\217\357\270\217B'; exec sleep 100" || exit 1
sleep 1
$TMUX2 capturep -p -t "$PANE" >$TMP || exit 1
EXPECTED=$(printf 'A\342\234\217\357\270\217B')
check_line 2 "$EXPECTED"
$TMUX2 killw -t "$WINDOW" || exit 1
# Long line, then short line: default cells after cellsize must clear stale text.
capture
check_line 1 "abcdefghijklmnopqrst"

View File

@@ -17,7 +17,8 @@
# unlink the last link without -k;
# - swap-window within and between sessions, -d keeping the active window,
# and the grouped-sessions error;
# - rotate-window -U/-D rotating pane positions;
# - rotate-window -U/-D rotating pane positions, with and without preserving
# zoom using -Z;
# - kill-window switching to the last (previously current) window, kill-window
# -a killing all other windows and the "-f only valid with -a" guard.
#
@@ -306,6 +307,24 @@ check_fmt 'R:0' '#{pane_index}:#{pane_id}' "0:$p1"
check_ok rotate-window -D -t R:0
check_fmt 'R:0' '#{pane_index}:#{pane_id}' "0:$p0"
# Rotation without -Z unzooms. With -Z it preserves zoom and transfers it to
# the pane which arrives at the active position.
layout=$($TMUX display-message -p -t R:0 '#{window_layout}')
check_ok resize-pane -Z -t "$p0"
check_ok rotate-window -U -t R:0
check_fmt "$p1" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'0:0:1'
check_ok rotate-window -D -t R:0
check_fmt 'R:0' '#{window_layout}' "$layout"
check_ok resize-pane -Z -t "$p0"
check_ok rotate-window -U -Z -t R:0
check_fmt "$p1" '#{window_zoomed_flag}:#{pane_zoomed_flag}:#{pane_active}' \
'1:1:1'
check_ok resize-pane -Z -t "$p1"
check_ok rotate-window -D -t R:0
check_fmt 'R:0' '#{window_layout}' "$layout"
# ---------------------------------------------------------------------------
# kill-window.