From d1dd18952bf127e02a0b3dfcfc10d2a16716e13b Mon Sep 17 00:00:00 2001 From: Michael Grant Date: Sat, 18 Jul 2026 21:47:37 +0100 Subject: [PATCH] Fixed cursor preservation during collapse/uncollapse. --- regress/collapse-output.sh | 12 ++++ window-copy.c | 136 ++++++++++++++++++++++++------------- 2 files changed, 100 insertions(+), 48 deletions(-) diff --git a/regress/collapse-output.sh b/regress/collapse-output.sh index 770d32f81..71c358419 100644 --- a/regress/collapse-output.sh +++ b/regress/collapse-output.sh @@ -47,6 +47,18 @@ $TMUX kill-server $TMUX new-session -d -x80 -y20 "sh -c 'printf \"\\033]133;A\\007P> \\033]133;B\\007for i in 1 2 3; do\\n\\033]133;A;k=s\\007> \\033]133;B\\007echo \\$i\\n\\033]133;A;k=s\\007> \\033]133;B\\007done\\033]133;C\\007\\nRESULT\\n\\033]133;D;0\\007\\n\\033]133;A\\007P> \\033]133;B\\007\"; exec sleep 100'" || exit 1 sleep 1 +$TMUX set-window-option copy-mode-collapse CD || exit 1 +$TMUX copy-mode -U || exit 1 +$TMUX send-keys -X search-backward 'P> for i in 1 2 3; do' || exit 1 +$TMUX send-keys -X end-of-line || exit 1 +x=$($TMUX display-message -p '#{copy_cursor_x}') +$TMUX send-keys Tab || exit 1 +line=$($TMUX display-message -p '#{copy_cursor_line}') +[ "$line" = 'P> for i in 1 2 3; do' ] || exit 1 +newx=$($TMUX display-message -p '#{copy_cursor_x}') +[ "$newx" = "$x" ] || exit 1 +$TMUX send-keys -X cancel || exit 1 + $TMUX set-window-option copy-mode-collapse AC || exit 1 $TMUX copy-mode -U || exit 1 $TMUX send-keys -X search-backward 'P> for i in 1 2 3; do' || exit 1 diff --git a/window-copy.c b/window-copy.c index 761b1104b..a1c9bb897 100644 --- a/window-copy.c +++ b/window-copy.c @@ -3502,57 +3502,111 @@ window_copy_cmd_line_numbers_toggle(struct window_copy_cmd_state *cs) } static int -window_copy_find_output(struct window_mode_entry *wme, u_int *prompt, - u_int *chosen) +window_copy_find_output(struct window_mode_entry *wme, u_int target, + u_int *prompt, u_int *chosen) { struct window_copy_mode_data *data = wme->data; struct grid *gd = data->source->grid; struct grid_line *gl; - u_int target, last_prompt = 0, output = UINT_MAX, y; - int command = 0; + u_int candidate = UINT_MAX, last_prompt = 0; + u_int output = UINT_MAX, y; + int command = 0, have_prompt = 0; *prompt = 0; *chosen = UINT_MAX; - gl = grid_get_line(data->backing->grid, - data->backing->grid->hsize + data->cy - data->oy); - target = gl->source_line; - if (gl->flags & (GRID_LINE_OUTPUT_CONTROL|GRID_LINE_OUTPUT_MEMBER)) { - *chosen = gl->output_line; - for (y = 0; y <= *chosen; y++) { - if (grid_get_line(gd, y)->flags & GRID_LINE_START_PROMPT) - *prompt = y; - } - return (1); - } for (y = 0; y < gd->hsize + gd->sy; y++) { gl = grid_get_line(gd, y); if (gl->flags & GRID_LINE_START_PROMPT) { + if (have_prompt && target >= last_prompt && target < y && + candidate != UINT_MAX) { + *prompt = last_prompt; + *chosen = candidate; + return (1); + } last_prompt = y; + candidate = UINT_MAX; + output = UINT_MAX; command = 0; + have_prompt = 1; } if (gl->flags & GRID_LINE_START_COMMAND) command = 1; if (gl->flags & GRID_LINE_START_OUTPUT) { output = y; - *prompt = last_prompt; + candidate = y; } if (gl->flags & GRID_LINE_END_OUTPUT) { - if (output != UINT_MAX) { - if (target >= *prompt && target <= y) { - *chosen = output; - return (1); - } - } else if (command && target >= last_prompt && target <= y) { - *prompt = last_prompt; - *chosen = y; - return (1); - } + if (output == UINT_MAX && command) + candidate = y; output = UINT_MAX; command = 0; } } - if (output != UINT_MAX && target >= *prompt) { - *chosen = output; + if (have_prompt && target >= last_prompt && candidate != UINT_MAX) { + *prompt = last_prompt; + *chosen = candidate; + return (1); + } + return (0); +} + +static void +window_copy_cursor_source(struct window_mode_entry *wme, u_int *source_x, + u_int *source_y) +{ + struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->backing->grid; + struct grid_line *gl; + u_int y; + + *source_x = data->cx; + y = gd->hsize + data->cy - data->oy; + if (!data->output_controls) + *source_y = y; + else { + gl = grid_get_line(gd, y); + *source_y = gl->source_line; + } +} + +static int +window_copy_restore_cursor(struct window_mode_entry *wme, u_int source_x, + u_int source_y, u_int prompt) +{ + struct window_copy_mode_data *data = wme->data; + struct grid *gd = data->backing->grid; + struct grid_line *gl; + u_int y; + + for (y = 0; y < gd->hsize + gd->sy; y++) { + gl = grid_get_line(gd, y); + if (gl->source_line != source_y || gl->cellused == 0) + continue; + data->cx = source_x; + if (data->cx >= gl->cellused) + data->cx = gl->cellused - 1; + if (y < gd->hsize) { + data->cy = 0; + data->oy = gd->hsize - y; + } else { + data->cy = y - gd->hsize; + data->oy = 0; + } + return (1); + } + for (y = 0; y < gd->hsize + gd->sy; y++) { + gl = grid_get_line(gd, y); + if (gl->source_line != prompt || + !(gl->flags & GRID_LINE_OUTPUT_CONTROL)) + continue; + data->cx = gl->command; + if (y < gd->hsize) { + data->cy = 0; + data->oy = gd->hsize - y; + } else { + data->cy = y - gd->hsize; + data->oy = 0; + } return (1); } return (0); @@ -3564,18 +3618,20 @@ window_copy_set_output(struct window_mode_entry *wme, int all, int collapse) struct window_copy_mode_data *data = wme->data; struct grid *gd; struct grid_line *gl; - u_int prompt = 0, y, chosen = UINT_MAX; + u_int prompt = 0, source_x, source_y; + u_int chosen = UINT_MAX; int changed = 0, enabled = 0, wanted; if (data->source == NULL) return (0); + window_copy_cursor_source(wme, &source_x, &source_y); if (!data->output_controls) { data->output_controls = 1; window_copy_rebuild_backing(wme); enabled = 1; } gd = data->source->grid; - window_copy_find_output(wme, &prompt, &chosen); + window_copy_find_output(wme, source_y, &prompt, &chosen); if (all) { wanted = collapse; if (wanted == -1) @@ -3607,24 +3663,8 @@ window_copy_set_output(struct window_mode_entry *wme, int all, int collapse) window_copy_clear_selection(wme); window_copy_clear_marks(wme); window_copy_rebuild_backing(wme); - if (chosen != UINT_MAX) { - for (y = 0; y < data->backing->grid->hsize + - data->backing->grid->sy; y++) { - gl = grid_get_line(data->backing->grid, y); - if (gl->source_line != prompt || - !(gl->flags & GRID_LINE_OUTPUT_CONTROL)) - continue; - data->cx = gl->command; - if (y < data->backing->grid->hsize) { - data->cy = 0; - data->oy = data->backing->grid->hsize - y; - } else { - data->cy = y - data->backing->grid->hsize; - data->oy = 0; - } - return (1); - } - } + if (window_copy_restore_cursor(wme, source_x, source_y, prompt)) + return (1); data->cy = screen_size_y(&data->screen) - 1; data->cx = 0; data->oy = 0;