Fix OSC 133 output range lookup for markers sharing a line

Handle C and D on the same line, output ending on the next prompt's
line, and output starting on a prompt line. Keep the output columns
when A resets a line's prompt data, and fall back to the previous
output if the range is empty. Extend the regress test to cover these
and reset the paste buffer before each check.
This commit is contained in:
Michael Grant
2026-09-29 10:03:12 +01:00
parent 1612a88282
commit 0a82ccc16c
3 changed files with 102 additions and 22 deletions

View File

@@ -3278,8 +3278,8 @@ input_osc_133(struct input_ctx *ictx, const char *p)
case 'A':
case 'N':
if (gl != NULL) {
memset(&gl->osc133_data, 0, sizeof gl->osc133_data);
gl->osc133_data.prompt_col = s->cx;
gl->osc133_data.cmd_col = 0;
gl->flags |= GRID_LINE_START_PROMPT;
}
if (wp != NULL) {

View File

@@ -20,6 +20,7 @@ $TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007p\\$ \\033]133;B
sleep 1
$TMUX copy-mode || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -X copy-output || exit 1
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
@@ -33,6 +34,7 @@ sleep 1
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
$TMUX send-keys -X select-output || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = unfinished ] || exit 1
$TMUX send-keys -X cancel || exit 1
@@ -46,12 +48,14 @@ $TMUX send-keys -X cancel || exit 1
$TMUX copy-mode || exit 1
$TMUX send-keys -X search-backward separator || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -X copy-output || exit 1
[ "$($TMUX show-buffer)" = "$EXPECTED" ] || exit 1
$TMUX send-keys -X cancel || exit 1
$TMUX copy-mode || exit 1
$TMUX send-keys -X search-backward one || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -X copy-output || exit 1
[ "$($TMUX show-buffer)" = "$EXPECTED" ] || exit 1
$TMUX send-keys -X cancel || exit 1
@@ -59,6 +63,7 @@ $TMUX send-keys -X cancel || exit 1
$TMUX new-window -d -n plain "printf 'alpha\\nbeta\\n'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :plain || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :plain.0 -X copy-output -a || exit 1
all=$($TMUX show-buffer)
case "$all" in
@@ -69,13 +74,15 @@ $TMUX send-keys -t :plain.0 -X cancel || exit 1
$TMUX set-buffer -b keep unchanged || exit 1
$TMUX copy-mode -t :plain || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :plain.0 -X copy-output || exit 1
[ "$($TMUX show-buffer -b keep)" = unchanged ] || exit 1
$TMUX send-keys -t :plain.0 -X cancel || exit 1
$TMUX new-window -d -n empty "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\033]133;C\\007one\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007true\\033]133;C\\033]133;D;0\\007'; exec sleep 100" || exit 1
$TMUX new-window -d -n empty "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\033]133;C\\007one\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007true\\n\\033]133;C\\007\\033]133;D;0\\007'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :empty || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :empty.0 -X copy-output || exit 1
[ "$($TMUX show-buffer)" = one ] || exit 1
$TMUX send-keys -t :empty.0 -X cancel || exit 1
@@ -83,6 +90,7 @@ $TMUX send-keys -t :empty.0 -X cancel || exit 1
$TMUX new-window -d -n prompt "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\033]133;C\\007one\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :prompt || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :prompt.0 -X copy-output || exit 1
[ "$($TMUX show-buffer)" = one ] || exit 1
$TMUX send-keys -t :prompt.0 -X cancel || exit 1
@@ -91,6 +99,7 @@ $TMUX copy-mode -t :prompt || exit 1
$TMUX send-keys -t :prompt.0 -X -N 100 cursor-down || exit 1
$TMUX send-keys -t :prompt.0 C-o || exit 1
[ "$($TMUX display-message -p -t :prompt.0 '#{selection_present}')" = 1 ] || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :prompt.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = one ] || exit 1
$TMUX send-keys -t :prompt.0 -X cancel || exit 1
@@ -100,6 +109,7 @@ sleep 1
$TMUX copy-mode -t :same || exit 1
$TMUX send-keys -t :same.0 -X search-backward one || exit 1
$TMUX send-keys -t :same.0 -X select-output || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :same.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = one ] || exit 1
$TMUX send-keys -t :same.0 -X cancel || exit 1
@@ -108,8 +118,41 @@ $TMUX new-window -d -n clear "printf 'old1\\nold2\\nold3\\nold4\\nold5\\nold6\\n
sleep 1
$TMUX copy-mode -t :clear || exit 1
$TMUX send-keys -t :clear.0 C-o || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :clear.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = "$(printf 'PID TTY\n1 pts/0')" ] || exit 1
[ "$($TMUX show-buffer)" = "$(printf '1\nPID TTY\n1 pts/0')" ] || exit 1
$TMUX send-keys -t :clear.0 -X cancel || exit 1
$TMUX new-window -d -n oneline "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\n\\033]133;C\\007one\\033]133;D;0\\007\\nseparator\\n\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :oneline || exit 1
$TMUX send-keys -t :oneline.0 -X search-backward echo || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :oneline.0 -X copy-output || exit 1
[ "$($TMUX show-buffer)" = one ] || exit 1
$TMUX send-keys -t :oneline.0 -X cancel || exit 1
$TMUX new-window -d -n nextprompt "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo\\n\\033]133;C\\007one\\ntwo\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :nextprompt || exit 1
$TMUX send-keys -t :nextprompt.0 -X search-backward echo || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :nextprompt.0 -X copy-output || exit 1
[ "$($TMUX show-buffer)" = "$EXPECTED" ] || exit 1
$TMUX send-keys -t :nextprompt.0 -X cancel || exit 1
$TMUX new-window -d -n hist "printf '\\033]133;A\\007p\\$ \\033]133;B\\007seq\\n\\033]133;C\\007'; seq 1 40; sleep 3; seq 100 103; printf '\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
sleep 1
$TMUX clear-history -t :hist.0 || exit 1
sleep 4
$TMUX copy-mode -t :hist || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :hist.0 -X copy-output || exit 1
$TMUX show-buffer >$OUT
[ "$(head -n1 "$OUT")" != sentinel ] || exit 1
[ "$(head -n1 "$OUT")" != 1 ] || exit 1
[ "$(tail -n1 "$OUT")" = 103 ] || exit 1
grep -q seq "$OUT" && exit 1
$TMUX send-keys -t :hist.0 -X cancel || exit 1
exit 0

View File

@@ -6236,6 +6236,9 @@ window_copy_find_output_range(struct window_mode_entry *wme, u_int *sx,
u_int cursor_x, cursor_y, prompt_x = 0;
u_int prompt_y = UINT_MAX, y, total;
int found_start = 0, found_end = 0;
int next_prompt, in_range, found;
void *buf;
size_t len;
cursor_x = data->cx;
cursor_y = screen_hsize(data->backing) + data->cy - data->oy;
@@ -6260,27 +6263,38 @@ window_copy_find_output_range(struct window_mode_entry *wme, u_int *sx,
for (y = prompt_y; y < total; y++) {
gl = grid_get_line(gd, y);
/* An output may end on the next prompt's line. */
next_prompt = (y != prompt_y &&
gl->flags & GRID_LINE_START_PROMPT);
/* Output before the next prompt on its line is ours. */
if (y == prompt_y)
in_range = (gl->osc133_data.out_start_col >= prompt_x);
else if (next_prompt)
in_range = (gl->osc133_data.out_start_col <
gl->osc133_data.prompt_col);
else
in_range = 1;
if (gl->flags & GRID_LINE_START_OUTPUT && in_range) {
*sx = gl->osc133_data.out_start_col;
*sy = y;
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 || gl->osc133_data.out_end_col >= prompt_x)) {
(y != prompt_y || gl->osc133_data.out_end_col >= prompt_x) &&
(y != *sy || gl->osc133_data.out_end_col >= *sx)) {
*ex = gl->osc133_data.out_end_col;
*ey = y;
found_end = 1;
break;
}
if (y != prompt_y && gl->flags & GRID_LINE_START_PROMPT)
if (next_prompt)
break;
if (gl->flags & GRID_LINE_START_OUTPUT &&
(y != prompt_y || gl->osc133_data.out_start_col >= prompt_x)) {
*sx = gl->osc133_data.out_start_col;
*sy = y;
found_start = 1;
}
}
if (!found_start) {
log_debug("%s: no output after prompt", __func__);
return (window_copy_find_previous_output_range(wme, cursor_x,
cursor_y, sx, sy, ex, ey));
found = window_copy_find_previous_output_range(wme, cursor_x,
cursor_y, sx, sy, ex, ey);
return (found);
}
if (!found_end) {
if (y != total) {
@@ -6289,6 +6303,14 @@ window_copy_find_output_range(struct window_mode_entry *wme, u_int *sx,
}
window_copy_output_end(data->backing, ex, ey);
}
buf = window_copy_get_grid_range(wme, *sx, *sy, *ex, *ey, &len);
if (buf == NULL) {
log_debug("%s: empty output", __func__);
found = window_copy_find_previous_output_range(wme, cursor_x,
cursor_y, sx, sy, ex, ey);
return (found);
}
free(buf);
log_debug("%s: output from %u,%u to %u,%u", __func__, *sx, *sy,
*ex, *ey);
return (1);
@@ -6303,19 +6325,28 @@ window_copy_find_previous_output_range(struct window_mode_entry *wme,
struct window_copy_mode_data *data = wme->data;
struct grid *gd = data->backing->grid;
struct grid_line *gl;
struct osc133_data *od;
void *buf;
u_int start_x, start_y, end_x, end_y;
u_int y, total;
size_t len;
int found = 0, have_prompt = 0;
int pending = 0;
int has_start, has_end, end_first;
total = gd->hsize + gd->sy;
for (y = 0; y < total && y <= cursor_y; y++) {
gl = grid_get_line(gd, y);
if (gl->flags & GRID_LINE_START_OUTPUT &&
(y != cursor_y || gl->osc133_data.out_start_col <= cursor_x)) {
start_x = gl->osc133_data.out_start_col;
od = &gl->osc133_data;
has_start = (gl->flags & GRID_LINE_START_OUTPUT &&
(y != cursor_y || od->out_start_col <= cursor_x));
has_end = (gl->flags & GRID_LINE_END_OUTPUT &&
(y != cursor_y || od->out_end_col <= cursor_x));
/* An end before the start ends the previous output. */
end_first = (has_start && has_end &&
od->out_end_col < od->out_start_col);
if (has_start && !end_first) {
start_x = od->out_start_col;
start_y = y;
pending = 1;
}
@@ -6323,13 +6354,12 @@ window_copy_find_previous_output_range(struct window_mode_entry *wme,
if (!pending && !have_prompt &&
gl->flags & GRID_LINE_END_OUTPUT &&
(~gl->flags & GRID_LINE_START_PROMPT ||
gl->osc133_data.out_end_col <= gl->osc133_data.prompt_col)) {
od->out_end_col <= od->prompt_col)) {
start_x = start_y = 0;
pending = 1;
}
if (pending && gl->flags & GRID_LINE_END_OUTPUT &&
(y != cursor_y || gl->osc133_data.out_end_col <= cursor_x)) {
end_x = gl->osc133_data.out_end_col;
if (pending && has_end) {
end_x = od->out_end_col;
end_y = y;
buf = window_copy_get_grid_range(wme, start_x, start_y,
end_x, end_y, &len);
@@ -6343,8 +6373,15 @@ window_copy_find_previous_output_range(struct window_mode_entry *wme,
}
pending = 0;
}
if (has_start && end_first) {
start_x = od->out_start_col;
start_y = y;
pending = 1;
}
if (gl->flags & GRID_LINE_START_PROMPT) {
pending = 0;
/* Output may start after the prompt. */
if (!has_start || od->out_start_col < od->prompt_col)
pending = 0;
have_prompt = 1;
}
}