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 0da1c84c53
commit 1632bbe8ce
3 changed files with 348 additions and 105 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. */
struct input_ctx {
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_112(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. */
static void input_clear(struct input_ctx *);
@@ -1466,6 +1477,7 @@ input_csi_dispatch(struct input_ctx *ictx)
struct screen *s = sctx->s;
const struct input_table_entry *entry;
struct options *oo;
struct input_osc_133_ctx osc133 = {0};
int i, n, m, ek, set, p;
u_int cx, bg = ictx->cell.cell.bg;
@@ -1739,13 +1751,19 @@ input_csi_dispatch(struct input_ctx *ictx)
case -1:
break;
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);
input_osc_133_restore_marker(ictx, &osc133);
break;
case 1:
screen_write_clearstartofscreen(sctx, bg);
break;
case 2:
osc133 = input_osc_133_save_marker(ictx);
screen_write_clearscreen(sctx, bg);
input_osc_133_restore_marker(ictx, &osc133);
break;
case 3:
if (input_get(ictx, 1, 0, 0) == 0) {
@@ -3278,6 +3296,77 @@ input_osc_133_secondary_prompt(const char *p)
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. */
static void
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) {
case 'A':
case 'N':
if (gl != NULL) {
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;
}
}
input_osc_133_mark_prompt(gl, s->cx, p);
if (wp != NULL) {
wp->last_prompt_time = time(NULL);
events_fire_pane("pane-shell-prompt", wp);
}
break;
case 'P':
if (gl != NULL) {
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;
}
}
input_osc_133_mark_prompt(gl, s->cx, p);
break;
case 'B':
case 'I':

View File

@@ -140,6 +140,103 @@ $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
# 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
sleep 1
$TMUX copy-mode -t :oneline || exit 1

View File

@@ -27,6 +27,7 @@
#include "tmux.h"
struct window_copy_mode_data;
struct window_copy_fold_request;
#define WINDOW_COPY_LINE_CONTROL 0x1
#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 *,
struct screen_write_ctx *, u_int, u_int);
static void window_copy_rebuild_backing(struct window_mode_entry *);
static int window_copy_set_fold(struct window_mode_entry *, int, int,
u_int, int);
static int window_copy_set_fold(struct window_mode_entry *,
const struct window_copy_fold_request *);
static int window_copy_find_fold(struct window_mode_entry *, u_int,
u_int *, u_int *);
static void window_copy_cursor_source(struct window_mode_entry *, u_int *,
@@ -285,6 +286,32 @@ struct window_copy_cmd_state {
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 {
u_int source_line;
u_int output_line;
@@ -707,11 +734,12 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
struct screen_write_ctx ctx;
struct grid_cell gc;
struct osc133_data *osc133;
u_int y, x, total, dy, prompt_dy = UINT_MAX;
u_int output_line = UINT_MAX;
int collapsed = 0, command = 0, hiding = 0, tree = 0;
int in_output = 0, output_empty = 0;
int initial = 0, was_hiding;
struct window_copy_fold_ctx fold = {
.prompt_dy = UINT_MAX,
.output_line = UINT_MAX
};
u_int y, x, total, dy;
int was_hiding;
if (src == NULL)
return;
@@ -751,7 +779,7 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
* 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.
*/
initial = 1;
fold.initial = 1;
screen_write_start(&ctx, dst);
/*
@@ -764,21 +792,22 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
osc133 = &sgl->osc133_data;
for (x = 0; x <= sgl->cellused; x++) {
dy = dst->grid->hsize + dst->cy;
was_hiding = hiding;
if (!hiding)
was_hiding = fold.hiding;
if (!fold.hiding)
/*
* Row is visible. Records the source
* row and current output-block identity
* for cursor restoration, selection,
* 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
* there may be markers to process.
*/
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.
* Attach controls to top line, make it a
* "synthetic command" so top lines can fold.
@@ -793,17 +822,19 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
*/
dgl->flags |= GRID_LINE_START_OUTPUT;
dgl->osc133_data.out_start_col = dst->cx;
output_line = y;
tree = 1; /* used to draw the | or L in gutter */
in_output = 1;
output_empty = 1;
collapsed = data->source_flags[y] &
fold.output_line = y;
/* used to draw the | or L in gutter */
fold.tree = 1;
fold.in_output = 1;
fold.output_empty = 1;
fold.collapsed = data->source_flags[y] &
WINDOW_COPY_FOLD_COLLAPSED;
hiding = collapsed; /* Start/stop hiding cells */
if (prompt_dy != UINT_MAX)
/* Start/stop hiding cells */
fold.hiding = fold.collapsed;
if (fold.prompt_dy != UINT_MAX)
/* Attach +/- controls to the prompt. */
window_copy_set_control(data, prompt_dy,
output_line, 1);
window_copy_set_control(data,
fold.prompt_dy, fold.output_line, 1);
}
if ((sgl->flags & GRID_LINE_END_OUTPUT) &&
x == osc133->out_end_col) {
@@ -819,56 +850,60 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
dgl->flags |= GRID_LINE_END_OUTPUT_STATUS;
dgl->osc133_data.out_end_col = dst->cx;
dgl->osc133_data.exit_status = osc133->exit_status;
if (in_output && output_empty && prompt_dy != UINT_MAX) {
line = window_copy_get_line_info(data, prompt_dy);
if (fold.in_output && fold.output_empty &&
fold.prompt_dy != UINT_MAX) {
line = window_copy_get_line_info(data,
fold.prompt_dy);
if (line != NULL)
/* No output to hide, no +/-. */
line->flags &= ~WINDOW_COPY_LINE_EXPANDABLE;
}
in_output = 0;
if (output_line == UINT_MAX && command) {
fold.in_output = 0;
if (fold.output_line == UINT_MAX &&
fold.command) {
/*
* Saw osc133 B but not C marker.
* This could be user pressed enter
* or command like 'true' with no
* output.
*/
output_line = y;
tree = 1;
fold.output_line = y;
fold.tree = 1;
/* Block is collapsed? */
collapsed = data->source_flags[y] &
fold.collapsed = data->source_flags[y] &
WINDOW_COPY_FOLD_COLLAPSED;
if (prompt_dy != UINT_MAX)
if (fold.prompt_dy != UINT_MAX)
/*
* Make this a +/- control row
* but without the +/- for now
* since we don't know what
* comes below.
*/
window_copy_set_control(data, prompt_dy,
output_line, 0);
window_copy_set_control(data,
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)
/*
* osc133 D, record exit status on the
* command's prompt row.
*/
window_copy_set_exit_status(data, prompt_dy,
osc133->exit_status);
if (initial && prompt_dy == UINT_MAX)
window_copy_set_exit_status(data,
fold.prompt_dy, osc133->exit_status);
if (fold.initial && fold.prompt_dy == UINT_MAX)
/* At top of buffer before first osc A
* marker use first line's fold state.
*/
hiding = data->source_flags[0] &
fold.hiding = data->source_flags[0] &
WINDOW_COPY_FOLD_COLLAPSED;
else
/*
* Otherwise, use the current block's
* fold state.
*/
hiding = collapsed;
command = 0;
fold.hiding = fold.collapsed;
fold.command = 0;
}
/*
* Process osc133 C and D before a following prompt
@@ -879,10 +914,11 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
/* osc133 A ends region, starts new prompt. */
dgl->flags |= GRID_LINE_START_PROMPT;
dgl->osc133_data.prompt_col = dst->cx;
prompt_dy = dy;
output_line = UINT_MAX;
collapsed = command = hiding = tree = 0;
in_output = output_empty = 0;
fold.prompt_dy = dy;
fold.output_line = UINT_MAX;
fold.collapsed = fold.command = 0;
fold.hiding = fold.tree = 0;
fold.in_output = fold.output_empty = 0;
}
if ((sgl->flags & GRID_LINE_SECOND_PROMPT) &&
x == osc133->prompt_col) {
@@ -895,44 +931,48 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
/* osc133 B marks start/continue of command. */
dgl->flags |= GRID_LINE_START_COMMAND;
dgl->osc133_data.cmd_col = dst->cx;
command = 1;
fold.command = 1;
}
/*
* An osc133 A may expose the remainder of this source
* line after a folded region.
*/
if (was_hiding && !hiding)
window_copy_set_line(data, dy, y, output_line, 0);
if (was_hiding && !fold.hiding)
window_copy_set_line(data, dy, y,
fold.output_line, 0);
if (x == sgl->cellused)
break;
grid_get_cell(sgd, x, y, &gc);
if (in_output && !(gc.flags & GRID_FLAG_PADDING))
output_empty = 0;
if (fold.in_output && !(gc.flags & GRID_FLAG_PADDING))
fold.output_empty = 0;
/* Separator text after D is also expandable. */
if (!in_output && tree && output_line != UINT_MAX &&
!(gc.flags & GRID_FLAG_PADDING) && prompt_dy != UINT_MAX)
window_copy_set_control(data, prompt_dy, output_line, 1);
if (!fold.in_output && fold.tree &&
fold.output_line != UINT_MAX &&
!(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
* cell output does not.
*/
if (hiding)
if (fold.hiding)
continue;
if (!(gc.flags & GRID_FLAG_PADDING)) {
screen_write_cell(&ctx, &gc);
if (tree)
window_copy_set_line(data, dy, y, output_line,
WINDOW_COPY_LINE_MEMBER);
if (fold.tree)
window_copy_set_line(data, dy, y,
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 |
GRID_LINE_START_OUTPUT | GRID_LINE_END_OUTPUT))
/* 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);
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
* synthetic command line (top line of copy buffer).
@@ -940,14 +980,14 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
screen_write_carriagereturn(&ctx);
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)
* becomes our "synthisied" commmand to anchor lines
* folded below it up to first osc133 A.
*/
tree = 1;
hiding = data->source_flags[0] &
fold.tree = 1;
fold.hiding = data->source_flags[0] &
WINDOW_COPY_FOLD_COLLAPSED;
}
}
@@ -2143,6 +2183,9 @@ window_copy_cmd_select_output(struct window_copy_cmd_state *cs)
{
struct window_mode_entry *wme = cs->wme;
struct window_copy_mode_data *data = wme->data;
struct window_copy_fold_request request = {
.action = WINDOW_COPY_FOLD_EXPAND
};
struct grid_reader gr;
void *buf;
size_t len;
@@ -2166,8 +2209,10 @@ window_copy_cmd_select_output(struct window_copy_cmd_state *cs)
window_copy_set_all_folds(data, 0);
window_copy_rebuild_backing(wme);
} else if (data->fold_view &&
data->source_flags[sy] & WINDOW_COPY_FOLD_COLLAPSED)
window_copy_set_fold(wme, 0, 0, sy, 0);
data->source_flags[sy] & WINDOW_COPY_FOLD_COLLAPSED) {
request.target = sy;
window_copy_set_fold(wme, &request);
}
if (!window_copy_source_position(wme, sx, sy, &bsx, &bsy) ||
!window_copy_source_position(wme, ex, ey, &bex, &bey))
return (WINDOW_COPY_CMD_NOTHING);
@@ -4304,12 +4349,13 @@ window_copy_restore_before(struct window_mode_entry *wme, u_int source_y)
}
static int
window_copy_set_fold(struct window_mode_entry *wme, int all, int collapse,
u_int target, int command_end)
window_copy_set_fold(struct window_mode_entry *wme,
const struct window_copy_fold_request *request)
{
struct window_copy_mode_data *data = wme->data;
u_int prompt = 0, source_x, source_y;
u_int chosen = UINT_MAX, first_prompt, y;
u_int target = request->target;
int changed = 0, enabled = 0, found, initial;
int wanted;
@@ -4335,22 +4381,22 @@ window_copy_set_fold(struct window_mode_entry *wme, int all, int collapse,
enabled = 1;
}
found = window_copy_find_fold(wme, target, &prompt, &chosen);
if (all) {
wanted = collapse;
if (wanted == -1)
if (request->all) {
wanted = (request->action == WINDOW_COPY_FOLD_COLLAPSE);
if (request->action == WINDOW_COPY_FOLD_TOGGLE)
wanted = !window_copy_any_fold_collapsed(data);
changed = window_copy_set_all_folds(data, wanted);
} else if (initial) {
wanted = collapse;
if (wanted == -1)
wanted = (request->action == WINDOW_COPY_FOLD_COLLAPSE);
if (request->action == WINDOW_COPY_FOLD_TOGGLE)
wanted = !(data->source_flags[0] &
WINDOW_COPY_FOLD_COLLAPSED);
if (window_copy_set_source_folded(data, 0, wanted)) {
changed = 1;
}
} else if (found && chosen != UINT_MAX) {
wanted = collapse;
if (wanted == -1)
wanted = (request->action == WINDOW_COPY_FOLD_COLLAPSE);
if (request->action == WINDOW_COPY_FOLD_TOGGLE)
wanted = !(data->source_flags[chosen] &
WINDOW_COPY_FOLD_COLLAPSED);
if (window_copy_set_source_folded(data, chosen, wanted)) {
@@ -4367,7 +4413,7 @@ window_copy_set_fold(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 (!command_end &&
if (!request->command_end &&
window_copy_restore_cursor(wme, source_x, source_y))
return (1);
if (initial && window_copy_restore_before(wme, 0))
@@ -4385,8 +4431,13 @@ window_copy_set_fold(struct window_mode_entry *wme, int all, int collapse,
static enum window_copy_cmd_action
window_copy_cmd_collapse_output(struct window_copy_cmd_state *cs)
{
if (window_copy_set_fold(cs->wme, args_has(cs->wargs, 'a'), 1,
UINT_MAX, 0))
struct window_copy_fold_request request = {
.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_NOTHING);
}
@@ -4394,8 +4445,13 @@ window_copy_cmd_collapse_output(struct window_copy_cmd_state *cs)
static enum window_copy_cmd_action
window_copy_cmd_expand_output(struct window_copy_cmd_state *cs)
{
if (window_copy_set_fold(cs->wme, args_has(cs->wargs, 'a'), 0,
UINT_MAX, 0))
struct window_copy_fold_request request = {
.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_NOTHING);
}
@@ -4405,9 +4461,14 @@ window_copy_cmd_toggle_output(struct window_copy_cmd_state *cs)
{
struct window_copy_mode_data *data = cs->wme->data;
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 target_prompt, chosen, x, y;
int command_end = 0, current_found;
int current_found;
int initial, target_found;
if (args_has(cs->wargs, 'm')) {
@@ -4426,13 +4487,11 @@ window_copy_cmd_toggle_output(struct window_copy_cmd_state *cs)
line->source_line, &target_prompt, &chosen);
if (!target_found)
return (WINDOW_COPY_CMD_NOTHING);
command_end = initial || !current_found ||
request.command_end = initial || !current_found ||
current_prompt != target_prompt;
target = line->source_line;
} else
target = UINT_MAX;
if (window_copy_set_fold(cs->wme, args_has(cs->wargs, 'a'), -1,
target, command_end))
request.target = line->source_line;
}
if (window_copy_set_fold(cs->wme, &request))
return (WINDOW_COPY_CMD_REDRAW);
return (WINDOW_COPY_CMD_NOTHING);
}
@@ -7291,6 +7350,7 @@ window_copy_find_output_range(struct window_mode_entry *wme, u_int *sx,
struct grid_line *gl;
u_int cursor_x, cursor_y, prompt_x = 0;
u_int prompt_y = UINT_MAX, y, total;
u_int output_x = 0, output_y = UINT_MAX;
int found_start = 0, found_end = 0;
int next_prompt, in_range, found;
@@ -7303,6 +7363,12 @@ window_copy_find_output_range(struct window_mode_entry *wme, u_int *sx,
total = gd->hsize + gd->sy;
for (y = 0; y < total; y++) {
gl = grid_get_line(gd, y);
if ((gl->flags & GRID_LINE_START_OUTPUT) &&
(y < cursor_y || (y == cursor_y &&
gl->osc133_data.out_start_col <= cursor_x))) {
output_x = gl->osc133_data.out_start_col;
output_y = y;
}
if (~gl->flags & GRID_LINE_START_PROMPT)
continue;
if (y > cursor_y ||
@@ -7311,11 +7377,18 @@ window_copy_find_output_range(struct window_mode_entry *wme, u_int *sx,
prompt_y = y;
prompt_x = gl->osc133_data.prompt_col;
}
/* C may survive without A, or have been restored after a clear. */
if (output_y != UINT_MAX && (prompt_y == UINT_MAX ||
output_y > prompt_y ||
(output_y == prompt_y && output_x >= prompt_x))) {
prompt_x = output_x;
prompt_y = output_y;
}
if (prompt_y == UINT_MAX) {
log_debug("%s: no osc133 prompt before cursor", __func__);
log_debug("%s: no osc133 marker before cursor", __func__);
return (0);
}
log_debug("%s: prompt at %u,%u", __func__, prompt_x, prompt_y);
log_debug("%s: start at %u,%u", __func__, prompt_x, prompt_y);
for (y = prompt_y; y < total; y++) {
gl = grid_get_line(gd, y);
@@ -7336,6 +7409,8 @@ window_copy_find_output_range(struct window_mode_entry *wme, u_int *sx,
}
/* An output may end on the same line or the next prompt's. */
if (found_start && gl->flags & GRID_LINE_END_OUTPUT &&
(!next_prompt || gl->osc133_data.out_end_col <=
gl->osc133_data.prompt_col) &&
(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;