Fix OSC 133 output ranges after history loss and trim vi selection endpoints.

This commit is contained in:
Michael Grant
2026-10-06 14:21:59 +02:00
parent 5503e34639
commit a0f7b38345
3 changed files with 103 additions and 42 deletions

View File

@@ -2,6 +2,43 @@
. ./input-common.inc
check_output()
{
name=$1
search=$2
for position in inside after; do
for keys in emacs vi; do
$TMUX set-option -w -t "$name:" mode-keys "$keys" ||
exit 1
$TMUX copy-mode -t "$name:" || exit 1
if [ "$position" = inside ]; then
$TMUX send-keys -t "$name:" -X search-backward \
"$search" || exit 1
fi
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t "$name:" -X copy-output || exit 1
$TMUX save-buffer "$TMP" || exit 1
cmp "$TMP" "$EXP" ||
fail "$name $position $keys output"
$TMUX send-keys -t "$name:" -X select-output || exit 1
present=$($TMUX display-message -p -t "$name:" \
'#{selection_present}')
if [ "$present" != 1 ]; then
echo "FAIL: $name $position $keys no selection"
exit_status=1
fi
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t "$name:" -X copy-selection || exit 1
$TMUX save-buffer "$TMP" || exit 1
cmp "$TMP" "$EXP" ||
fail "$name $position $keys selection"
$TMUX send-keys -t "$name:" -X cancel || exit 1
done
done
}
# A multiline command evicts its prompt before producing output.
command='\033]133;A\007p>\033]133;B\007echo\n'
i=0
@@ -30,50 +67,49 @@ for state in complete unfinished next-prompt; do
check_raw_matches lost 'END_OUTPUT.*osc133=0,0,0,3,0'
fi
for position in inside after; do
for keys in emacs vi; do
$TMUX set-option -w -t lost: mode-keys "$keys" ||
exit 1
$TMUX copy-mode -t lost: || exit 1
if [ "$position" = inside ]; then
$TMUX send-keys -t lost: -X search-backward one ||
exit 1
fi
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t lost: -X copy-output || exit 1
$TMUX save-buffer "$TMP" || exit 1
printf '%s' "$OUTPUT" >"$EXP"
cmp "$TMP" "$EXP" ||
fail "$state $position $keys output"
printf '%s' "$OUTPUT" >"$EXP"
check_output lost one
done
$TMUX send-keys -t lost: -X select-output || exit 1
present=$($TMUX display-message -p -t lost: \
'#{selection_present}')
if [ "$present" != 1 ]; then
echo "FAIL: $state $position $keys no selection"
exit_status=1
fi
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t lost: -X copy-selection || exit 1
$TMUX save-buffer "$TMP" || exit 1
cmp "$TMP" "$EXP" ||
fail "$state $position $keys selection"
$TMUX send-keys -t lost: -X cancel || exit 1
done
done
# A long output evicts both A and C but leaves D.
output='\033]133;A\007p>\033]133;B\007echo\n\033]133;C\007'
i=0
while [ "$i" -lt 30 ]; do
output="${output}$(printf 'row%02d' "$i")\n"
i=$((i + 1))
done
for state in complete next-prompt same-line-prompt; do
seq="${output}\033]133;D;0\007separator"
if [ "$state" != complete ]; then
if [ "$state" = next-prompt ]; then
seq="${seq}\n"
fi
seq="${seq}\033]133;A\007p>\033]133;B\007"
fi
start_pane_hlimit clipped 40 6 "$seq" 3
check_raw_no_matches clipped 'START_OUTPUT'
if [ "$state" = complete ]; then
check_raw_no_matches clipped 'START_PROMPT'
fi
check_raw_matches clipped 'END_OUTPUT'
$TMUX capture-pane -p -t clipped: -S - |
sed '/^separator/,$d' >"$EXP"
check_output clipped row29
done
# Retained command text does not belong to a later prompt's output.
seq="${command}\033]133;A\007p>\033]133;B\007echo\n"
seq="${seq}\033]133;C\007later\033]133;D;0\007"
start_pane_hlimit later 40 5 "$seq" 3
$TMUX copy-mode -t later: || exit 1
$TMUX send-keys -t later: -X history-top || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t later: -X copy-output || exit 1
[ "$($TMUX show-buffer)" = sentinel ] || exit 1
$TMUX send-keys -t later: -X select-output || exit 1
[ "$($TMUX display-message -p -t later: '#{selection_present}')" = 0 ] ||
exit 1
for newline in '' '\n'; do
seq="${command}\033]133;A\007p>\033]133;B\007echo${newline}"
seq="${seq}\033]133;C\007later\033]133;D;0\007"
start_pane_hlimit later 40 5 "$seq" 3
$TMUX copy-mode -t later: || exit 1
$TMUX send-keys -t later: -X history-top || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t later: -X copy-output || exit 1
[ "$($TMUX show-buffer)" = sentinel ] || exit 1
$TMUX send-keys -t later: -X select-output || exit 1
[ "$($TMUX display-message -p -t later: '#{selection_present}')" = 0 ] ||
exit 1
done
exit $exit_status

View File

@@ -142,4 +142,19 @@ check_output below "$expected"
check_selection below "$expected"
check_pipe below "$expected"
# Selection trims trailing cells without adding a newline in vi mode.
for suffix in ' \033]133;D;0\007' '\033[5C\033]133;D;0\007' ' '; do
start_pane_history trailing 80 20 \
"${prompt}\033]133;C\007one${suffix}"
check_output trailing one
check_selection trailing one
done
# A newline before D is part of the output, including after spaces.
start_pane_history newline 80 20 \
"${prompt}\033]133;C\007one \n\033]133;D;0\007"
expected=$(printf 'one\n_')
check_output newline "${expected%_}"
check_selection newline "${expected%_}"
exit $exit_status

View File

@@ -1608,7 +1608,7 @@ window_copy_cmd_select_output(struct window_copy_cmd_state *cs)
struct grid_reader gr;
void *buf;
size_t len;
u_int sx, sy, ex, ey, total;
u_int sx, sy, ex, ey, total, last;
int all = args_has(cs->wargs, 'a');
if (all) {
@@ -1631,6 +1631,9 @@ window_copy_cmd_select_output(struct window_copy_cmd_state *cs)
window_copy_start_selection(wme);
if (options_get_number(wme->wp->window->options, "mode-keys") ==
MODEKEY_VI) {
last = window_copy_find_length(wme, ey);
if (ex > last)
ex = last;
grid_reader_start(&gr, data->backing->grid, ex, ey);
grid_reader_cursor_left(&gr, 1);
grid_reader_get_cursor(&gr, &ex, &ey);
@@ -6276,6 +6279,13 @@ window_copy_find_output_range(struct window_mode_entry *wme, u_int *sx,
*sy = y;
found_start = 1;
}
/* Both A and C may have left history while D remains. */
if (!found_start && prompt_y == UINT_MAX &&
gl->flags & GRID_LINE_END_OUTPUT &&
(!next_prompt || od->out_end_col <= od->prompt_col)) {
*sx = *sy = 0;
found_start = 1;
}
/* An output may end on the same line or the next prompt's. */
if (found_start && gl->flags & GRID_LINE_END_OUTPUT &&
(y != prompt_y || od->out_end_col >= prompt_x) &&