Fix clear issue which lost C and A osc133 markers. Several cleanups.

This commit is contained in:
Michael Grant
2026-10-03 01:13:01 +01:00
parent cd0dc6e1a5
commit c35c98aef0
3 changed files with 330 additions and 103 deletions

111
input.c
View File

@@ -95,6 +95,14 @@ struct input_param {
}; };
}; };
/* Saved OSC 133 output marker. */
struct input_osc_133_ctx {
int running;
u_int start;
u_int col;
u_int collected;
};
/* Input parser context. */ /* Input parser context. */
struct input_ctx { struct input_ctx {
struct window_pane *wp; struct window_pane *wp;
@@ -173,6 +181,9 @@ static void input_osc_110(struct input_ctx *, const char *);
static void input_osc_111(struct input_ctx *, const char *); static void input_osc_111(struct input_ctx *, const char *);
static void input_osc_112(struct input_ctx *, const char *); static void input_osc_112(struct input_ctx *, const char *);
static void input_osc_133(struct input_ctx *, const char *); static void input_osc_133(struct input_ctx *, const char *);
static struct input_osc_133_ctx input_osc_133_save_marker(struct input_ctx *);
static void input_osc_133_restore_marker(struct input_ctx *,
const struct input_osc_133_ctx *);
/* Transition entry/exit handlers. */ /* Transition entry/exit handlers. */
static void input_clear(struct input_ctx *); static void input_clear(struct input_ctx *);
@@ -1466,6 +1477,7 @@ input_csi_dispatch(struct input_ctx *ictx)
struct screen *s = sctx->s; struct screen *s = sctx->s;
const struct input_table_entry *entry; const struct input_table_entry *entry;
struct options *oo; struct options *oo;
struct input_osc_133_ctx osc133 = {0};
int i, n, m, ek, set, p; int i, n, m, ek, set, p;
u_int cx, bg = ictx->cell.cell.bg; u_int cx, bg = ictx->cell.cell.bg;
@@ -1739,13 +1751,19 @@ input_csi_dispatch(struct input_ctx *ictx)
case -1: case -1:
break; break;
case 0: case 0:
/* Erasing to the end clears the whole screen at 0,0. */
if (s->cx == 0 && s->cy == 0)
osc133 = input_osc_133_save_marker(ictx);
screen_write_clearendofscreen(sctx, bg); screen_write_clearendofscreen(sctx, bg);
input_osc_133_restore_marker(ictx, &osc133);
break; break;
case 1: case 1:
screen_write_clearstartofscreen(sctx, bg); screen_write_clearstartofscreen(sctx, bg);
break; break;
case 2: case 2:
osc133 = input_osc_133_save_marker(ictx);
screen_write_clearscreen(sctx, bg); screen_write_clearscreen(sctx, bg);
input_osc_133_restore_marker(ictx, &osc133);
break; break;
case 3: case 3:
if (input_get(ictx, 1, 0, 0) == 0) { if (input_get(ictx, 1, 0, 0) == 0) {
@@ -3278,6 +3296,77 @@ input_osc_133_secondary_prompt(const char *p)
return (0); return (0);
} }
/* Record the first primary or secondary prompt marker on a line. */
static void
input_osc_133_mark_prompt(struct grid_line *gl, u_int col, const char *p)
{
if (gl == NULL || gl->flags &
(GRID_LINE_START_PROMPT|GRID_LINE_SECOND_PROMPT))
return;
gl->osc133_data.prompt_col = col;
if (input_osc_133_secondary_prompt(p))
gl->flags |= GRID_LINE_SECOND_PROMPT;
else
gl->flags |= GRID_LINE_START_PROMPT;
}
/* Save the running command's output start before clearing the screen. */
static struct input_osc_133_ctx
input_osc_133_save_marker(struct input_ctx *ictx)
{
struct window_pane *wp = ictx->wp;
struct screen *s = ictx->ctx.s;
struct grid *gd = s->grid;
struct grid_line *gl;
struct input_osc_133_ctx ctx = {0};
u_int y;
if (wp == NULL || s != &wp->base || SCREEN_IS_ALTERNATE(s) ||
(~wp->flags & PANE_CMDRUNNING))
return (ctx);
ctx.running = 1;
ctx.start = UINT_MAX;
ctx.collected = gd->scroll_collected;
for (y = gd->hsize + gd->sy; y > 0; y--) {
gl = grid_get_line(gd, y - 1);
if ((gl->flags & GRID_LINE_START_OUTPUT) &&
(~gl->flags & GRID_LINE_END_OUTPUT ||
gl->osc133_data.out_end_col < gl->osc133_data.out_start_col) &&
(~gl->flags & GRID_LINE_START_PROMPT ||
gl->osc133_data.prompt_col <= gl->osc133_data.out_start_col)) {
ctx.start = y - 1;
ctx.col = gl->osc133_data.out_start_col;
break;
}
if (gl->flags & (GRID_LINE_START_PROMPT|GRID_LINE_END_OUTPUT))
break;
}
return (ctx);
}
/* Restore the running command's output start if a screen clear removed it. */
static void
input_osc_133_restore_marker(struct input_ctx *ictx,
const struct input_osc_133_ctx *ctx)
{
struct grid *gd = ictx->ctx.s->grid;
struct grid_line *gl;
u_int collected;
if (!ctx->running)
return;
collected = gd->scroll_collected - ctx->collected;
if (ctx->start != UINT_MAX && ctx->start >= collected) {
gl = grid_get_line(gd, ctx->start - collected);
if ((gl->flags & GRID_LINE_START_OUTPUT) &&
gl->osc133_data.out_start_col == ctx->col)
return;
}
gl = grid_get_line(gd, gd->hsize);
gl->flags |= GRID_LINE_START_OUTPUT;
gl->osc133_data.out_start_col = 0;
}
/* Handle the OSC 133 sequence. */ /* Handle the OSC 133 sequence. */
static void static void
input_osc_133(struct input_ctx *ictx, const char *p) input_osc_133(struct input_ctx *ictx, const char *p)
@@ -3295,32 +3384,14 @@ input_osc_133(struct input_ctx *ictx, const char *p)
switch (*p) { switch (*p) {
case 'A': case 'A':
case 'N': case 'N':
if (gl != NULL) { input_osc_133_mark_prompt(gl, s->cx, p);
if (!(gl->flags & (GRID_LINE_START_PROMPT|
GRID_LINE_SECOND_PROMPT))) {
gl->osc133_data.prompt_col = s->cx;
if (input_osc_133_secondary_prompt(p))
gl->flags |= GRID_LINE_SECOND_PROMPT;
else
gl->flags |= GRID_LINE_START_PROMPT;
}
}
if (wp != NULL) { if (wp != NULL) {
wp->last_prompt_time = time(NULL); wp->last_prompt_time = time(NULL);
events_fire_pane("pane-shell-prompt", wp); events_fire_pane("pane-shell-prompt", wp);
} }
break; break;
case 'P': case 'P':
if (gl != NULL) { input_osc_133_mark_prompt(gl, s->cx, p);
if (!(gl->flags & (GRID_LINE_START_PROMPT|
GRID_LINE_SECOND_PROMPT))) {
gl->osc133_data.prompt_col = s->cx;
if (input_osc_133_secondary_prompt(p))
gl->flags |= GRID_LINE_SECOND_PROMPT;
else
gl->flags |= GRID_LINE_START_PROMPT;
}
}
break; break;
case 'B': case 'B':
case 'I': case 'I':

View File

@@ -169,6 +169,103 @@ $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 'PID TTY\n1 pts/0')" ] || exit 1
$TMUX send-keys -t :clear.0 -X cancel || exit 1 $TMUX send-keys -t :clear.0 -X cancel || exit 1
# Select from either output line after clearing the screen.
for view in normal unfolded; do
for steps in 2 3; do
if [ "$view" = unfolded ]; then
$TMUX copy-mode -U -t :clear || exit 1
else
$TMUX copy-mode -t :clear || exit 1
fi
$TMUX send-keys -t :clear.0 -X -N "$steps" cursor-up || exit 1
$TMUX send-keys -t :clear.0 C-o || exit 1
[ "$($TMUX display-message -p -t :clear.0 '#{selection_present}')" = 1 ] || 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 send-keys -t :clear.0 -X cancel || exit 1
done
done
$TMUX set-option -g scroll-on-clear on || exit 1
$TMUX new-window -d -n clearprompt "printf '\\033]133;A\\007p\\$ \\033]133;B\\007clear; echo hello; echo world\\n\\033]133;C\\007\\033[H\\033[2J\\033[3Jhello\\nworld\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
sleep 1
for steps in 1 2; do
$TMUX copy-mode -t :clearprompt || exit 1
$TMUX send-keys -t :clearprompt.0 -X -N "$steps" cursor-up || exit 1
$TMUX send-keys -t :clearprompt.0 C-o || exit 1
[ "$($TMUX display-message -p -t :clearprompt.0 '#{selection_present}')" = 1 ] || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :clearprompt.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = "$(printf 'hello\nworld')" ] || exit 1
$TMUX send-keys -t :clearprompt.0 -X cancel || exit 1
done
# A screen clear must not pull earlier commands or plain scrollback into output.
for integration in plain marked; do
for erase in J 2J; do
$TMUX set-option -g scroll-on-clear off || exit 1
if [ "$integration" = marked ]; then
prefix='\033]133;A\007old>\033]133;B\007seq\n\033]133;C\007'
ending='\033]133;D;0\007'
else
prefix=
ending=
fi
$TMUX new-window -d -n retained "printf '$prefix'; seq 1 40; printf '$ending\\033]133;A\\007p\\$ \\033]133;B\\007clear; echo hello; echo world\\n\\033]133;C\\007\\033[H\\033[${erase}hello\\nworld\\n\\033]133;D;0\\007\\033]133;A\\007p\\$ \\033]133;B\\007'; exec sleep 100" || exit 1
sleep 1
[ "$($TMUX display-message -p -t :retained.0 '#{history_size}')" -gt 0 ] || exit 1
for steps in 0 1 2; do
$TMUX copy-mode -t :retained || exit 1
if [ "$steps" -gt 0 ]; then
$TMUX send-keys -t :retained.0 -X -N "$steps" cursor-up || exit 1
fi
$TMUX send-keys -t :retained.0 C-o || exit 1
[ "$($TMUX display-message -p -t :retained.0 '#{selection_present}')" = 1 ] || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :retained.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = "$(printf 'hello\nworld')" ] || exit 1
$TMUX send-keys -t :retained.0 -X cancel || exit 1
done
$TMUX kill-window -t :retained || exit 1
done
done
# Preserve C when scroll-on-clear keeps the command's earlier output.
$TMUX set-option -g scroll-on-clear on || exit 1
$TMUX new-window -d -n preserve "printf '\\033]133;A\\007p\\$ \\033]133;B\\007echo before; clear; echo hello; echo world\\n\\033]133;C\\007before\\n\\033[H\\033[2Jhello\\nworld\\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 :preserve || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :preserve.0 -X copy-output || exit 1
[ "$($TMUX show-buffer)" = "$(printf 'before\nhello\nworld')" ] || exit 1
$TMUX send-keys -t :preserve.0 -X cancel || exit 1
# An unfinished command can be selected using its restored C without D.
$TMUX set-option -g scroll-on-clear off || exit 1
$TMUX new-window -d -n running "seq 1 40; printf '\\033]133;A\\007p\\$ \\033]133;B\\007clear; echo hello; echo world\\n\\033]133;C\\007\\033[H\\033[2Jhello\\nworld\\n'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :running || exit 1
$TMUX send-keys -t :running.0 -X cursor-up || exit 1
$TMUX send-keys -t :running.0 C-o || exit 1
[ "$($TMUX display-message -p -t :running.0 '#{selection_present}')" = 1 ] || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :running.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = "$(printf 'hello\nworld')" ] || exit 1
$TMUX send-keys -t :running.0 -X cancel || exit 1
# Do not infer output from a D which belongs to the first surviving prompt.
$TMUX new-window -d -n prefix "printf 'prefix\\n\\033]133;A\\007p\\$ \\033]133;B\\007echo\\033]133;C\\007one\\033]133;D;0\\007'; exec sleep 100" || exit 1
sleep 1
$TMUX copy-mode -t :prefix || exit 1
$TMUX send-keys -t :prefix.0 -X cursor-up || exit 1
$TMUX send-keys -t :prefix.0 C-o || exit 1
[ "$($TMUX display-message -p -t :prefix.0 '#{selection_present}')" = 0 ] || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :prefix.0 -X copy-output || exit 1
[ "$($TMUX show-buffer)" = sentinel ] || exit 1
$TMUX send-keys -t :prefix.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 $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 sleep 1
$TMUX copy-mode -t :oneline || exit 1 $TMUX copy-mode -t :oneline || exit 1

View File

@@ -27,6 +27,7 @@
#include "tmux.h" #include "tmux.h"
struct window_copy_mode_data; struct window_copy_mode_data;
struct window_copy_fold_request;
#define WINDOW_COPY_LINE_CONTROL 0x1 #define WINDOW_COPY_LINE_CONTROL 0x1
#define WINDOW_COPY_LINE_MEMBER 0x2 #define WINDOW_COPY_LINE_MEMBER 0x2
@@ -82,8 +83,8 @@ static void window_copy_write_line(struct window_mode_entry *,
static void window_copy_write_lines(struct window_mode_entry *, static void window_copy_write_lines(struct window_mode_entry *,
struct screen_write_ctx *, u_int, u_int); struct screen_write_ctx *, u_int, u_int);
static void window_copy_rebuild_backing(struct window_mode_entry *); static void window_copy_rebuild_backing(struct window_mode_entry *);
static int window_copy_set_fold(struct window_mode_entry *, int, int, static int window_copy_set_fold(struct window_mode_entry *,
u_int, int); const struct window_copy_fold_request *);
static int window_copy_find_fold(struct window_mode_entry *, u_int, static int window_copy_find_fold(struct window_mode_entry *, u_int,
u_int *, u_int *); u_int *, u_int *);
static void window_copy_cursor_source(struct window_mode_entry *, u_int *, static void window_copy_cursor_source(struct window_mode_entry *, u_int *,
@@ -288,6 +289,32 @@ struct window_copy_cmd_state {
struct winlink *wl; struct winlink *wl;
}; };
enum window_copy_fold_action {
WINDOW_COPY_FOLD_COLLAPSE,
WINDOW_COPY_FOLD_EXPAND,
WINDOW_COPY_FOLD_TOGGLE,
};
struct window_copy_fold_request {
enum window_copy_fold_action action;
int all;
u_int target;
int command_end;
};
/* State carried across source lines while rebuilding the folded screen. */
struct window_copy_fold_ctx {
u_int prompt_dy;
u_int output_line;
int collapsed;
int command;
int hiding;
int tree;
int in_output;
int output_empty;
int initial;
};
struct window_copy_line { struct window_copy_line {
u_int source_line; u_int source_line;
u_int output_line; u_int output_line;
@@ -714,11 +741,12 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
struct screen_write_ctx ctx; struct screen_write_ctx ctx;
struct grid_cell gc; struct grid_cell gc;
struct osc133_data *osc133; struct osc133_data *osc133;
u_int y, x, total, dy, prompt_dy = UINT_MAX; struct window_copy_fold_ctx fold = {
u_int output_line = UINT_MAX; .prompt_dy = UINT_MAX,
int collapsed = 0, command = 0, hiding = 0, tree = 0; .output_line = UINT_MAX
int in_output = 0, output_empty = 0; };
int initial = 0, was_hiding; u_int y, x, total, dy;
int was_hiding;
if (src == NULL) if (src == NULL)
return; return;
@@ -758,7 +786,7 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
* The source begins in an existing command or its output. Treat its * The source begins in an existing command or its output. Treat its
* first line as a synthetic command for the region before the first A. * first line as a synthetic command for the region before the first A.
*/ */
initial = 1; fold.initial = 1;
screen_write_start(&ctx, dst); screen_write_start(&ctx, dst);
/* /*
@@ -771,21 +799,22 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
osc133 = &sgl->osc133_data; osc133 = &sgl->osc133_data;
for (x = 0; x <= sgl->cellused; x++) { for (x = 0; x <= sgl->cellused; x++) {
dy = dst->grid->hsize + dst->cy; dy = dst->grid->hsize + dst->cy;
was_hiding = hiding; was_hiding = fold.hiding;
if (!hiding) if (!fold.hiding)
/* /*
* Row is visible. Records the source * Row is visible. Records the source
* row and current output-block identity * row and current output-block identity
* for cursor restoration, selection, * for cursor restoration, selection,
* gutter drawing, and line numbers. * gutter drawing, and line numbers.
*/ */
window_copy_set_line(data, dy, y, output_line, 0); window_copy_set_line(data, dy, y,
fold.output_line, 0);
/* /*
* Continue even if row is folded because * Continue even if row is folded because
* there may be markers to process. * there may be markers to process.
*/ */
dgl = grid_get_line(dst->grid, dy); dgl = grid_get_line(dst->grid, dy);
if (initial && y == 0 && x == 0) if (fold.initial && y == 0 && x == 0)
/* Top line of buffer, no osc133 marker yet. /* Top line of buffer, no osc133 marker yet.
* Attach controls to top line, make it a * Attach controls to top line, make it a
* "synthetic command" so top lines can fold. * "synthetic command" so top lines can fold.
@@ -800,17 +829,19 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
*/ */
dgl->flags |= GRID_LINE_START_OUTPUT; dgl->flags |= GRID_LINE_START_OUTPUT;
dgl->osc133_data.out_start_col = dst->cx; dgl->osc133_data.out_start_col = dst->cx;
output_line = y; fold.output_line = y;
tree = 1; /* used to draw the | or L in gutter */ /* used to draw the | or L in gutter */
in_output = 1; fold.tree = 1;
output_empty = 1; fold.in_output = 1;
collapsed = data->source_flags[y] & fold.output_empty = 1;
fold.collapsed = data->source_flags[y] &
WINDOW_COPY_FOLD_COLLAPSED; WINDOW_COPY_FOLD_COLLAPSED;
hiding = collapsed; /* Start/stop hiding cells */ /* Start/stop hiding cells */
if (prompt_dy != UINT_MAX) fold.hiding = fold.collapsed;
if (fold.prompt_dy != UINT_MAX)
/* Attach +/- controls to the prompt. */ /* Attach +/- controls to the prompt. */
window_copy_set_control(data, prompt_dy, window_copy_set_control(data,
output_line, 1); fold.prompt_dy, fold.output_line, 1);
} }
if ((sgl->flags & GRID_LINE_END_OUTPUT) && if ((sgl->flags & GRID_LINE_END_OUTPUT) &&
x == osc133->out_end_col) { x == osc133->out_end_col) {
@@ -826,56 +857,60 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
dgl->flags |= GRID_LINE_END_OUTPUT_STATUS; dgl->flags |= GRID_LINE_END_OUTPUT_STATUS;
dgl->osc133_data.out_end_col = dst->cx; dgl->osc133_data.out_end_col = dst->cx;
dgl->osc133_data.exit_status = osc133->exit_status; dgl->osc133_data.exit_status = osc133->exit_status;
if (in_output && output_empty && prompt_dy != UINT_MAX) { if (fold.in_output && fold.output_empty &&
line = window_copy_get_line_info(data, prompt_dy); fold.prompt_dy != UINT_MAX) {
line = window_copy_get_line_info(data,
fold.prompt_dy);
if (line != NULL) if (line != NULL)
/* No output to hide, no +/-. */ /* No output to hide, no +/-. */
line->flags &= ~WINDOW_COPY_LINE_EXPANDABLE; line->flags &= ~WINDOW_COPY_LINE_EXPANDABLE;
} }
in_output = 0; fold.in_output = 0;
if (output_line == UINT_MAX && command) { if (fold.output_line == UINT_MAX &&
fold.command) {
/* /*
* Saw osc133 B but not C marker. * Saw osc133 B but not C marker.
* This could be user pressed enter * This could be user pressed enter
* or command like 'true' with no * or command like 'true' with no
* output. * output.
*/ */
output_line = y; fold.output_line = y;
tree = 1; fold.tree = 1;
/* Block is collapsed? */ /* Block is collapsed? */
collapsed = data->source_flags[y] & fold.collapsed = data->source_flags[y] &
WINDOW_COPY_FOLD_COLLAPSED; WINDOW_COPY_FOLD_COLLAPSED;
if (prompt_dy != UINT_MAX) if (fold.prompt_dy != UINT_MAX)
/* /*
* Make this a +/- control row * Make this a +/- control row
* but without the +/- for now * but without the +/- for now
* since we don't know what * since we don't know what
* comes below. * comes below.
*/ */
window_copy_set_control(data, prompt_dy, window_copy_set_control(data,
output_line, 0); fold.prompt_dy,
fold.output_line, 0);
} }
if (prompt_dy != UINT_MAX && if (fold.prompt_dy != UINT_MAX &&
sgl->flags & GRID_LINE_END_OUTPUT_STATUS) sgl->flags & GRID_LINE_END_OUTPUT_STATUS)
/* /*
* osc133 D, record exit status on the * osc133 D, record exit status on the
* command's prompt row. * command's prompt row.
*/ */
window_copy_set_exit_status(data, prompt_dy, window_copy_set_exit_status(data,
osc133->exit_status); fold.prompt_dy, osc133->exit_status);
if (initial && prompt_dy == UINT_MAX) if (fold.initial && fold.prompt_dy == UINT_MAX)
/* At top of buffer before first osc A /* At top of buffer before first osc A
* marker use first line's fold state. * marker use first line's fold state.
*/ */
hiding = data->source_flags[0] & fold.hiding = data->source_flags[0] &
WINDOW_COPY_FOLD_COLLAPSED; WINDOW_COPY_FOLD_COLLAPSED;
else else
/* /*
* Otherwise, use the current block's * Otherwise, use the current block's
* fold state. * fold state.
*/ */
hiding = collapsed; fold.hiding = fold.collapsed;
command = 0; fold.command = 0;
} }
/* /*
* Process osc133 C and D before a following prompt * Process osc133 C and D before a following prompt
@@ -886,10 +921,11 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
/* osc133 A ends region, starts new prompt. */ /* osc133 A ends region, starts new prompt. */
dgl->flags |= GRID_LINE_START_PROMPT; dgl->flags |= GRID_LINE_START_PROMPT;
dgl->osc133_data.prompt_col = dst->cx; dgl->osc133_data.prompt_col = dst->cx;
prompt_dy = dy; fold.prompt_dy = dy;
output_line = UINT_MAX; fold.output_line = UINT_MAX;
collapsed = command = hiding = tree = 0; fold.collapsed = fold.command = 0;
in_output = output_empty = 0; fold.hiding = fold.tree = 0;
fold.in_output = fold.output_empty = 0;
} }
if ((sgl->flags & GRID_LINE_SECOND_PROMPT) && if ((sgl->flags & GRID_LINE_SECOND_PROMPT) &&
x == osc133->prompt_col) { x == osc133->prompt_col) {
@@ -902,44 +938,48 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
/* osc133 B marks start/continue of command. */ /* osc133 B marks start/continue of command. */
dgl->flags |= GRID_LINE_START_COMMAND; dgl->flags |= GRID_LINE_START_COMMAND;
dgl->osc133_data.cmd_col = dst->cx; dgl->osc133_data.cmd_col = dst->cx;
command = 1; fold.command = 1;
} }
/* /*
* An osc133 A may expose the remainder of this source * An osc133 A may expose the remainder of this source
* line after a folded region. * line after a folded region.
*/ */
if (was_hiding && !hiding) if (was_hiding && !fold.hiding)
window_copy_set_line(data, dy, y, output_line, 0); window_copy_set_line(data, dy, y,
fold.output_line, 0);
if (x == sgl->cellused) if (x == sgl->cellused)
break; break;
grid_get_cell(sgd, x, y, &gc); grid_get_cell(sgd, x, y, &gc);
if (in_output && !(gc.flags & GRID_FLAG_PADDING)) if (fold.in_output && !(gc.flags & GRID_FLAG_PADDING))
output_empty = 0; fold.output_empty = 0;
/* Separator text after D is also expandable. */ /* Separator text after D is also expandable. */
if (!in_output && tree && output_line != UINT_MAX && if (!fold.in_output && fold.tree &&
!(gc.flags & GRID_FLAG_PADDING) && prompt_dy != UINT_MAX) fold.output_line != UINT_MAX &&
window_copy_set_control(data, prompt_dy, output_line, 1); !(gc.flags & GRID_FLAG_PADDING) &&
fold.prompt_dy != UINT_MAX)
window_copy_set_control(data, fold.prompt_dy,
fold.output_line, 1);
/* /*
* osc133 marker processing continues while folded, but * osc133 marker processing continues while folded, but
* cell output does not. * cell output does not.
*/ */
if (hiding) if (fold.hiding)
continue; continue;
if (!(gc.flags & GRID_FLAG_PADDING)) { if (!(gc.flags & GRID_FLAG_PADDING)) {
screen_write_cell(&ctx, &gc); screen_write_cell(&ctx, &gc);
if (tree) if (fold.tree)
window_copy_set_line(data, dy, y, output_line, window_copy_set_line(data, dy, y,
WINDOW_COPY_LINE_MEMBER); fold.output_line, WINDOW_COPY_LINE_MEMBER);
} }
} }
if (!hiding && tree && sgl->cellused == 0 && if (!fold.hiding && fold.tree && sgl->cellused == 0 &&
~(sgl->flags) & (GRID_LINE_START_PROMPT | ~(sgl->flags) & (GRID_LINE_START_PROMPT |
GRID_LINE_START_OUTPUT | GRID_LINE_END_OUTPUT)) GRID_LINE_START_OUTPUT | GRID_LINE_END_OUTPUT))
/* Mark empty visible lines so gutter tree continues */ /* Mark empty visible lines so gutter tree continues */
window_copy_set_line(data, dy, y, output_line, window_copy_set_line(data, dy, y, fold.output_line,
WINDOW_COPY_LINE_MEMBER); WINDOW_COPY_LINE_MEMBER);
if (y + 1 != total && !(sgl->flags & GRID_LINE_WRAPPED) && if (y + 1 != total && !(sgl->flags & GRID_LINE_WRAPPED) &&
(!hiding || (initial && y == 0))) { (!fold.hiding || (fold.initial && y == 0))) {
/* /*
* Hide folded line breaks, except after the initial * Hide folded line breaks, except after the initial
* synthetic command line (top line of copy buffer). * synthetic command line (top line of copy buffer).
@@ -947,14 +987,14 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
screen_write_carriagereturn(&ctx); screen_write_carriagereturn(&ctx);
screen_write_linefeed(&ctx, 0, 8); screen_write_linefeed(&ctx, 0, 8);
} }
if (initial && y == 0) { if (fold.initial && y == 0) {
/* /*
* Top line of buffer (which may not be a command) * Top line of buffer (which may not be a command)
* becomes our "synthisied" commmand to anchor lines * becomes our "synthisied" commmand to anchor lines
* folded below it up to first osc133 A. * folded below it up to first osc133 A.
*/ */
tree = 1; fold.tree = 1;
hiding = data->source_flags[0] & fold.hiding = data->source_flags[0] &
WINDOW_COPY_FOLD_COLLAPSED; WINDOW_COPY_FOLD_COLLAPSED;
} }
} }
@@ -2161,6 +2201,9 @@ window_copy_cmd_select_output(struct window_copy_cmd_state *cs)
{ {
struct window_mode_entry *wme = cs->wme; struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data; struct window_copy_mode_data *data = wme->data;
struct window_copy_fold_request request = {
.action = WINDOW_COPY_FOLD_EXPAND
};
struct grid_reader gr; struct grid_reader gr;
void *buf; void *buf;
size_t len; size_t len;
@@ -2181,8 +2224,10 @@ window_copy_cmd_select_output(struct window_copy_cmd_state *cs)
window_copy_set_all_folds(data, 0); window_copy_set_all_folds(data, 0);
window_copy_rebuild_backing(wme); window_copy_rebuild_backing(wme);
} else if (data->fold_view && } else if (data->fold_view &&
data->source_flags[sy] & WINDOW_COPY_FOLD_COLLAPSED) data->source_flags[sy] & WINDOW_COPY_FOLD_COLLAPSED) {
window_copy_set_fold(wme, 0, 0, sy, 0); request.target = sy;
window_copy_set_fold(wme, &request);
}
if (!window_copy_source_position(wme, sx, sy, &bsx, &bsy) || if (!window_copy_source_position(wme, sx, sy, &bsx, &bsy) ||
!window_copy_source_position(wme, ex, ey, &bex, &bey)) !window_copy_source_position(wme, ex, ey, &bex, &bey))
return (WINDOW_COPY_CMD_NOTHING); return (WINDOW_COPY_CMD_NOTHING);
@@ -4322,12 +4367,13 @@ window_copy_restore_before(struct window_mode_entry *wme, u_int source_y)
} }
static int static int
window_copy_set_fold(struct window_mode_entry *wme, int all, int collapse, window_copy_set_fold(struct window_mode_entry *wme,
u_int target, int command_end) const struct window_copy_fold_request *request)
{ {
struct window_copy_mode_data *data = wme->data; struct window_copy_mode_data *data = wme->data;
u_int prompt = 0, source_x, source_y; u_int prompt = 0, source_x, source_y;
u_int chosen = UINT_MAX, first_prompt, y; u_int chosen = UINT_MAX, first_prompt, y;
u_int target = request->target;
int changed = 0, enabled = 0, found, initial; int changed = 0, enabled = 0, found, initial;
int wanted; int wanted;
@@ -4353,22 +4399,22 @@ window_copy_set_fold(struct window_mode_entry *wme, int all, int collapse,
enabled = 1; enabled = 1;
} }
found = window_copy_find_fold(wme, target, &prompt, &chosen); found = window_copy_find_fold(wme, target, &prompt, &chosen);
if (all) { if (request->all) {
wanted = collapse; wanted = (request->action == WINDOW_COPY_FOLD_COLLAPSE);
if (wanted == -1) if (request->action == WINDOW_COPY_FOLD_TOGGLE)
wanted = !window_copy_any_fold_collapsed(data); wanted = !window_copy_any_fold_collapsed(data);
changed = window_copy_set_all_folds(data, wanted); changed = window_copy_set_all_folds(data, wanted);
} else if (initial) { } else if (initial) {
wanted = collapse; wanted = (request->action == WINDOW_COPY_FOLD_COLLAPSE);
if (wanted == -1) if (request->action == WINDOW_COPY_FOLD_TOGGLE)
wanted = !(data->source_flags[0] & wanted = !(data->source_flags[0] &
WINDOW_COPY_FOLD_COLLAPSED); WINDOW_COPY_FOLD_COLLAPSED);
if (window_copy_set_source_folded(data, 0, wanted)) { if (window_copy_set_source_folded(data, 0, wanted)) {
changed = 1; changed = 1;
} }
} else if (found && chosen != UINT_MAX) { } else if (found && chosen != UINT_MAX) {
wanted = collapse; wanted = (request->action == WINDOW_COPY_FOLD_COLLAPSE);
if (wanted == -1) if (request->action == WINDOW_COPY_FOLD_TOGGLE)
wanted = !(data->source_flags[chosen] & wanted = !(data->source_flags[chosen] &
WINDOW_COPY_FOLD_COLLAPSED); WINDOW_COPY_FOLD_COLLAPSED);
if (window_copy_set_source_folded(data, chosen, wanted)) { if (window_copy_set_source_folded(data, chosen, wanted)) {
@@ -4385,7 +4431,7 @@ window_copy_set_fold(struct window_mode_entry *wme, int all, int collapse,
window_copy_clear_selection(wme); window_copy_clear_selection(wme);
window_copy_clear_marks(wme); window_copy_clear_marks(wme);
window_copy_rebuild_backing(wme); window_copy_rebuild_backing(wme);
if (!command_end && if (!request->command_end &&
window_copy_restore_cursor(wme, source_x, source_y)) window_copy_restore_cursor(wme, source_x, source_y))
return (1); return (1);
if (initial && window_copy_restore_before(wme, 0)) if (initial && window_copy_restore_before(wme, 0))
@@ -4403,8 +4449,13 @@ window_copy_set_fold(struct window_mode_entry *wme, int all, int collapse,
static enum window_copy_cmd_action static enum window_copy_cmd_action
window_copy_cmd_collapse_output(struct window_copy_cmd_state *cs) window_copy_cmd_collapse_output(struct window_copy_cmd_state *cs)
{ {
if (window_copy_set_fold(cs->wme, args_has(cs->wargs, 'a'), 1, struct window_copy_fold_request request = {
UINT_MAX, 0)) .action = WINDOW_COPY_FOLD_COLLAPSE,
.all = args_has(cs->wargs, 'a'),
.target = UINT_MAX
};
if (window_copy_set_fold(cs->wme, &request))
return (WINDOW_COPY_CMD_REDRAW); return (WINDOW_COPY_CMD_REDRAW);
return (WINDOW_COPY_CMD_NOTHING); return (WINDOW_COPY_CMD_NOTHING);
} }
@@ -4412,8 +4463,13 @@ window_copy_cmd_collapse_output(struct window_copy_cmd_state *cs)
static enum window_copy_cmd_action static enum window_copy_cmd_action
window_copy_cmd_expand_output(struct window_copy_cmd_state *cs) window_copy_cmd_expand_output(struct window_copy_cmd_state *cs)
{ {
if (window_copy_set_fold(cs->wme, args_has(cs->wargs, 'a'), 0, struct window_copy_fold_request request = {
UINT_MAX, 0)) .action = WINDOW_COPY_FOLD_EXPAND,
.all = args_has(cs->wargs, 'a'),
.target = UINT_MAX
};
if (window_copy_set_fold(cs->wme, &request))
return (WINDOW_COPY_CMD_REDRAW); return (WINDOW_COPY_CMD_REDRAW);
return (WINDOW_COPY_CMD_NOTHING); return (WINDOW_COPY_CMD_NOTHING);
} }
@@ -4423,9 +4479,14 @@ window_copy_cmd_toggle_output(struct window_copy_cmd_state *cs)
{ {
struct window_copy_mode_data *data = cs->wme->data; struct window_copy_mode_data *data = cs->wme->data;
struct window_copy_line *line; struct window_copy_line *line;
struct window_copy_fold_request request = {
.action = WINDOW_COPY_FOLD_TOGGLE,
.all = args_has(cs->wargs, 'a'),
.target = UINT_MAX
};
u_int current, current_prompt, target; u_int current, current_prompt, target;
u_int target_prompt, chosen, x, y; u_int target_prompt, chosen, x, y;
int command_end = 0, current_found; int current_found;
int initial, target_found; int initial, target_found;
if (args_has(cs->wargs, 'm')) { if (args_has(cs->wargs, 'm')) {
@@ -4444,13 +4505,11 @@ window_copy_cmd_toggle_output(struct window_copy_cmd_state *cs)
line->source_line, &target_prompt, &chosen); line->source_line, &target_prompt, &chosen);
if (!target_found) if (!target_found)
return (WINDOW_COPY_CMD_NOTHING); return (WINDOW_COPY_CMD_NOTHING);
command_end = initial || !current_found || request.command_end = initial || !current_found ||
current_prompt != target_prompt; current_prompt != target_prompt;
target = line->source_line; request.target = line->source_line;
} else }
target = UINT_MAX; if (window_copy_set_fold(cs->wme, &request))
if (window_copy_set_fold(cs->wme, args_has(cs->wargs, 'a'), -1,
target, command_end))
return (WINDOW_COPY_CMD_REDRAW); return (WINDOW_COPY_CMD_REDRAW);
return (WINDOW_COPY_CMD_NOTHING); return (WINDOW_COPY_CMD_NOTHING);
} }