More cleanup.

This commit is contained in:
Michael Grant
2026-10-03 11:44:19 +01:00
parent 0fe69462db
commit dd3839ead1
8 changed files with 281 additions and 302 deletions

34
input.c
View File

@@ -3199,7 +3199,8 @@ input_osc_133_save_marker(struct window_pane *wp, struct screen *s)
{
struct grid *gd = s->grid;
struct grid_line *gl;
struct input_osc_133_ctx ctx = {0};
struct osc133_data *od;
struct input_osc_133_ctx ctx = {0};
u_int y;
if (wp == NULL || s != &wp->base || SCREEN_IS_ALTERNATE(s) ||
@@ -3210,17 +3211,22 @@ input_osc_133_save_marker(struct window_pane *wp, struct screen *s)
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_OUTPUT) {
if (gl->flags &
(GRID_LINE_START_PROMPT|GRID_LINE_END_OUTPUT))
break;
continue;
}
if (gl->flags & (GRID_LINE_START_PROMPT|GRID_LINE_END_OUTPUT))
od = &gl->osc133_data;
if ((gl->flags & GRID_LINE_END_OUTPUT) &&
od->out_end_col >= od->out_start_col)
break;
if ((gl->flags & GRID_LINE_START_PROMPT) &&
od->prompt_col > od->out_start_col)
break;
ctx.start = y - 1;
ctx.col = od->out_start_col;
break;
}
return (ctx);
}
@@ -3252,9 +3258,8 @@ input_osc_133_restore_marker(struct screen *s,
static int
input_osc_133_exit_status(const char *p, int *present)
{
const char *end;
const char *end, *errstr;
char *copy;
char *endptr;
long long status;
*present = 0;
@@ -3272,10 +3277,9 @@ input_osc_133_exit_status(const char *p, int *present)
return (0);
}
*present = 1;
errno = 0;
status = (int)strtol(copy, &endptr, 10);
status = strtonum(copy, 0, 255, &errstr);
free(copy);
if (errno != 0 || endptr == copy || status < 0 || status > 255)
if (errstr != NULL)
return (255);
return (status);
}

View File

@@ -587,7 +587,7 @@ key_bindings_init(void)
"bind -Tcopy-mode C-[ { send -X cancel }",
"bind -Tcopy-mode Space { send -X page-down }",
"bind -Tcopy-mode Tab { send -X toggle-output }",
"bind -Tcopy-mode BTab { send -X toggle-output -a }",
"bind -Tcopy-mode BTab { send -X toggle-output -a }",
"bind -Tcopy-mode , { send -X jump-reverse }",
"bind -Tcopy-mode \\; { send -X jump-again }",
"bind -Tcopy-mode F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
@@ -668,7 +668,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi C-[ { send -X clear-selection }",
"bind -Tcopy-mode-vi Space { send -X begin-selection }",
"bind -Tcopy-mode-vi Tab { send -X toggle-output }",
"bind -Tcopy-mode-vi BTab { send -X toggle-output -a }",
"bind -Tcopy-mode-vi BTab { send -X toggle-output -a }",
"bind -Tcopy-mode-vi '$' { send -X end-of-line }",
"bind -Tcopy-mode-vi , { send -X jump-reverse }",
"bind -Tcopy-mode-vi / { command-prompt -P -T search -p'(search down)' { send -X search-forward -- '%%' } }",

View File

@@ -115,6 +115,7 @@ static const char *options_table_theme_list[] = {
static const char *options_table_copy_mode_line_numbers_list[] = {
"off", "default", "absolute", "relative", "hybrid", NULL
};
/* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \
"#[align=left range=left #{E:status-left-style}]" \

View File

@@ -84,7 +84,7 @@ check_grep " 3 !- p\$ two"
# A format wider than the standard three-column gutter is not truncated.
$TMUX2 send -X cancel || exit 1
$TMUX2 set -g copy-mode-exit-status-format \
$TMUX2 set -p copy-mode-exit-status-format \
'#[align=right]#{?exit_status,!#{exit_status}, }' || exit 1
$TMUX2 copy-mode -c || exit 1
$TMUX2 send -X history-top || exit 1

View File

@@ -26,5 +26,16 @@ check_raw_matches osc133 \
'L 7 \(7\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,2,5,255' \
'L 8 \(8\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,3,5,0'
# Reject malformed statuses and distinguish zero from a missing status.
start_pane osc133-status 20 8 \
'\033]133;C\007a\033]133;D;1junk\007\n\033]133;C\007b\033]133;D;4294967296\007\n\033]133;C\007c\033]133;D;255\007\n\033]133;C\007d\033]133;D;0;foo=bar\007\n\033]133;C\007e\033]133;D;foo=bar\007\n\033]133;C\007f\033]133;D;\007\n'
check_raw_matches osc133-status \
'L 0 \(0\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,0,1,255' \
'L 1 \(1\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,0,1,255' \
'L 2 \(2\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,0,1,255' \
'L 3 \(3\) flags=START_OUTPUT,END_OUTPUT,END_OUTPUT_STATUS\[[0-9a-f]+\].* osc133=0,0,0,1,0' \
'L 4 \(4\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,0,1,0' \
'L 5 \(5\) flags=START_OUTPUT,END_OUTPUT\[[0-9a-f]+\].* osc133=0,0,0,1,0'
$TMUX kill-server 2>/dev/null
exit $exit_status

View File

@@ -288,4 +288,18 @@ $TMUX show-buffer >$OUT
grep -q seq "$OUT" && exit 1
$TMUX send-keys -t :hist.0 -X cancel || exit 1
# View mode has no pane source snapshot.
$TMUX new-window -d -n view 'exec sleep 100' || exit 1
$TMUX run-shell -t :view.0 "printf 'view one\nview two\n'" || exit 1
[ "$($TMUX display-message -p -t :view.0 '#{pane_mode}')" = view-mode ] || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :view.0 -X copy-output -a || exit 1
[ "$($TMUX show-buffer)" = "$(printf 'view one\nview two')" ] || exit 1
$TMUX send-keys -t :view.0 -X select-output -a || exit 1
[ "$($TMUX display-message -p -t :view.0 '#{selection_present}')" = 1 ] || exit 1
$TMUX set-buffer sentinel || exit 1
$TMUX send-keys -t :view.0 -X copy-selection || exit 1
[ "$($TMUX show-buffer)" = "$(printf 'view one\nview two')" ] || exit 1
$TMUX send-keys -t :view.0 -X cancel || exit 1
exit 0

16
tmux.1
View File

@@ -2213,10 +2213,23 @@ Expand the output for the command on the cursor line.
With
.Fl a ,
expand all command output in the copy-mode buffer.
.Pp
.It Xo
.Ic toggle\-output
.Op Fl a
.Xc
Toggle whether output for the command on the cursor line is collapsed.
With
.Fl a ,
toggle all command output in the copy-mode buffer.
The Tab key toggles output for the command on the cursor line and S-Tab
toggles all command output.
.It Xo
.Ic fold\-view\-toggle
(vi: O)
(emacs: O)
.Xc
Toggle OSC 133 output controls in copy mode and expand all command output.
.It Xo
.Ic middle\-line
(vi: M)
(emacs: M\-r)
@@ -7434,6 +7447,7 @@ The following variables are available, where appropriate:
.It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode"
.It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode"
.It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode"
.It Li "copy_fold_view" Ta "" Ta "1 if OSC 133 output controls are shown"
.It Li "copy_line_numbers" Ta "" Ta "1 if line numbers are shown in copy mode"
.It Li "copy_position" Ta "" Ta "Position shown in the copy mode indicator"
.It Li "copy_position_limit" Ta "" Ta "Limit shown in the copy mode indicator"

View File

@@ -82,6 +82,9 @@ static void window_copy_write_line(struct window_mode_entry *,
struct screen_write_ctx *, u_int);
static void window_copy_write_lines(struct window_mode_entry *,
struct screen_write_ctx *, u_int, u_int);
static void window_copy_write_gutter(struct window_mode_entry *,
struct screen_write_ctx *, u_int, u_int,
const struct grid_cell *);
static void window_copy_rebuild_backing(struct window_mode_entry *);
static int window_copy_set_fold(struct window_mode_entry *,
const struct window_copy_fold_request *);
@@ -293,10 +296,10 @@ enum window_copy_fold_action {
};
struct window_copy_fold_request {
enum window_copy_fold_action action;
int all;
u_int target;
int command_end;
enum window_copy_fold_action action;
int all;
u_int target;
int command_end;
};
/* State carried across source lines while rebuilding the folded screen. */
@@ -335,12 +338,12 @@ struct window_copy_line {
* structure (which is deallocated when the mode ends).
*/
struct window_copy_mode_data {
struct screen screen;
struct screen screen;
struct screen *backing;
struct screen *source;
u_char *source_flags; /* Flags per source line */
u_int source_line_count;
struct screen *backing;
struct screen *source;
u_char *source_flags; /* flags per source line */
u_int source_line_count;
struct window_copy_line *lines;
u_int line_count;
int backing_written; /* backing display started */
@@ -560,6 +563,8 @@ window_copy_set_control(struct window_copy_mode_data *data, u_int y,
line->flags |= WINDOW_COPY_LINE_CONTROL;
if (expandable)
line->flags |= WINDOW_COPY_LINE_EXPANDABLE;
else
line->flags &= ~WINDOW_COPY_LINE_EXPANDABLE;
}
static void
@@ -583,10 +588,7 @@ window_copy_expand_exit_status(struct window_mode_entry *wme,
const char *value;
char *expanded;
/*
* This is called while rebuilding the backing grid, before the copy
* cursor and viewport have been restored for the new grid.
*/
/* The copy cursor and viewport have not been restored yet. */
ft = format_create(NULL, NULL, 0, 0);
format_add(ft, "exit_status", "0");
format_add(ft, "exit_status_present", "0");
@@ -594,7 +596,7 @@ window_copy_expand_exit_status(struct window_mode_entry *wme,
format_add(ft, "exit_status", "%hhu", line->exit_status);
format_add(ft, "exit_status_present", "1");
}
value = options_get_string(wme->wp->window->options,
value = options_get_string(wme->wp->options,
"copy-mode-exit-status-format");
expanded = format_expand(ft, value);
format_free(ft);
@@ -607,7 +609,7 @@ window_copy_update_exit_status_width(struct window_mode_entry *wme)
struct window_copy_mode_data *data = wme->data;
struct window_copy_line *line;
char *expanded;
u_int width, y;
u_int width, length, y;
data->output_status_width = 0;
if (!data->fold_view || data->lines == NULL)
@@ -618,8 +620,9 @@ window_copy_update_exit_status_width(struct window_mode_entry *wme)
if (~line->flags & WINDOW_COPY_LINE_CONTROL)
continue;
expanded = window_copy_expand_exit_status(wme, line);
if (format_width(expanded) > width)
width = format_width(expanded);
length = format_width(expanded);
if (length > width)
width = length;
free(expanded);
}
data->output_status_width = width;
@@ -638,6 +641,19 @@ window_copy_set_source_folded(struct window_copy_mode_data *data, u_int y,
return (1);
}
/* Find the first primary prompt, if any. */
static u_int
window_copy_first_prompt(struct grid *gd)
{
u_int y, total = gd->hsize + gd->sy;
for (y = 0; y < total; y++) {
if (grid_get_line(gd, y)->flags & GRID_LINE_START_PROMPT)
return (y);
}
return (UINT_MAX);
}
static int
window_copy_any_fold_collapsed(struct window_copy_mode_data *data)
{
@@ -646,14 +662,10 @@ window_copy_any_fold_collapsed(struct window_copy_mode_data *data)
u_int y;
int command = 0, output = 0;
for (y = 0; y < gd->hsize + gd->sy; y++) {
gl = grid_get_line(gd, y);
if (gl->flags & GRID_LINE_START_PROMPT)
break;
}
if (y != 0 && y != gd->hsize + gd->sy &&
y = window_copy_first_prompt(gd);
if (y != 0 && y != UINT_MAX &&
(data->source_flags[0] & WINDOW_COPY_FOLD_COLLAPSED))
/* Output which has scrolled off top of buffer is collapsed. */
/* The initial output region is collapsed. */
return (1);
for (y = 0; y < gd->hsize + gd->sy; y++) {
@@ -671,7 +683,8 @@ window_copy_any_fold_collapsed(struct window_copy_mode_data *data)
}
if (gl->flags & GRID_LINE_END_OUTPUT) {
if (!output && command &&
(data->source_flags[y] & WINDOW_COPY_FOLD_COLLAPSED))
(data->source_flags[y] &
WINDOW_COPY_FOLD_COLLAPSED))
return (1);
command = output = 0;
}
@@ -687,15 +700,10 @@ window_copy_set_all_folds(struct window_copy_mode_data *data, int collapse)
u_int y;
int changed = 0, command = 0, output = 0;
for (y = 0; y < gd->hsize + gd->sy; y++) {
gl = grid_get_line(gd, y);
if (gl->flags & GRID_LINE_START_PROMPT)
break;
}
if (y != 0 && y != gd->hsize + gd->sy &&
window_copy_set_source_folded(data, 0, collapse)) {
y = window_copy_first_prompt(gd);
if (y != 0 && y != UINT_MAX &&
window_copy_set_source_folded(data, 0, collapse))
changed = 1;
}
for (y = 0; y < gd->hsize + gd->sy; y++) {
gl = grid_get_line(gd, y);
@@ -706,16 +714,14 @@ window_copy_set_all_folds(struct window_copy_mode_data *data, int collapse)
if (gl->flags & GRID_LINE_START_COMMAND)
command = 1;
if (gl->flags & GRID_LINE_START_OUTPUT) {
if (window_copy_set_source_folded(data, y, collapse)) {
if (window_copy_set_source_folded(data, y, collapse))
changed = 1;
}
output = 1;
}
if (gl->flags & GRID_LINE_END_OUTPUT) {
if (!output && command &&
window_copy_set_source_folded(data, y, collapse)) {
window_copy_set_source_folded(data, y, collapse))
changed = 1;
}
command = output = 0;
}
}
@@ -730,7 +736,6 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
struct screen *src = data->source, *dst;
struct grid *sgd;
struct grid_line *sgl, *dgl;
struct window_copy_line *line;
struct screen_write_ctx ctx;
struct grid_cell gc;
struct osc133_data *osc133;
@@ -744,7 +749,8 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
if (src == NULL)
return;
if (!data->fold_view) {
dst = window_copy_clone_screen(src, &data->screen, NULL, NULL, 0);
dst = window_copy_clone_screen(src, &data->screen, NULL, NULL,
0);
if (data->backing != NULL) {
screen_free(data->backing);
free(data->backing);
@@ -759,8 +765,8 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
sgd = src->grid;
total = sgd->hsize + sgd->sy;
dst = xcalloc(1, sizeof *dst);
screen_init(dst, screen_size_x(&data->screen), screen_size_y(&data->screen),
screen_hlimit(src));
screen_init(dst, screen_size_x(&data->screen),
screen_size_y(&data->screen), screen_hlimit(src));
dst->grid->flags |= GRID_HISTORY;
free(data->lines);
data->lines = xcalloc(total, sizeof *data->lines);
@@ -769,24 +775,12 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
data->lines[y].source_line = UINT_MAX;
data->lines[y].output_line = UINT_MAX;
}
for (y = 0; y < total; y++) {
sgl = grid_get_line(sgd, y);
if (sgl->flags & GRID_LINE_START_PROMPT)
break;
}
if (y != 0 && y != total)
/*
* 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.
*/
fold.initial = 1;
y = window_copy_first_prompt(sgd);
/* Keep the first line as the control for any initial output. */
fold.initial = (y != 0 && y != UINT_MAX);
screen_write_start(&ctx, dst);
/*
* Markers may occur at any column, so copy the source one cell boundary at
* a time. The final boundary (cellused) is included to handle a marker
* immediately after the last cell on a line.
*/
/* Include the final cell boundary for markers after the last cell. */
for (y = 0; y < total; y++) {
sgl = grid_get_line(sgd, y);
osc133 = &sgl->osc133_data;
@@ -794,124 +788,81 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
dy = dst->grid->hsize + dst->cy;
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.
*/
/* Map visible rows to their source. */
window_copy_set_line(data, dy, y,
fold.output_line, 0);
/*
* Continue even if row is folded because
* there may be markers to process.
*/
/* Process markers on hidden rows too. */
dgl = grid_get_line(dst->grid, dy);
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.
*/
/* Attach initial output controls. */
window_copy_set_line(data, dy, y, UINT_MAX,
WINDOW_COPY_LINE_CONTROL | WINDOW_COPY_LINE_INITIAL);
WINDOW_COPY_LINE_CONTROL|
WINDOW_COPY_LINE_INITIAL);
if ((sgl->flags & GRID_LINE_START_OUTPUT) &&
x == osc133->out_start_col) {
/*
* osc133 C starts the collapsible region
* controlled by this source line.
*/
/* C starts the collapsible output region. */
dgl->flags |= GRID_LINE_START_OUTPUT;
dgl->osc133_data.out_start_col = dst->cx;
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;
/* Start/stop hiding cells */
fold.hiding = fold.collapsed;
if (fold.prompt_dy != UINT_MAX)
/* Attach +/- controls to the prompt. */
window_copy_set_control(data,
fold.prompt_dy, fold.output_line, 1);
fold.prompt_dy,
fold.output_line, 1);
}
if ((sgl->flags & GRID_LINE_END_OUTPUT) &&
x == osc133->out_end_col) {
/*
* Command output ends at the osc133 D
* marker. There may be more lines
* (like pre-prompt output) before next A
* marker. Fold these with previous output
* until the following A marker.
*/
/* Fold text after D until the next A. */
dgl->flags |= GRID_LINE_END_OUTPUT;
if (sgl->flags & GRID_LINE_END_OUTPUT_STATUS)
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.exit_status = osc133->exit_status;
dgl->osc133_data.exit_status =
osc133->exit_status;
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;
}
fold.prompt_dy != UINT_MAX)
/* No output to hide, no +/-. */
window_copy_set_control(data,
fold.prompt_dy,
fold.output_line, 0);
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.
*/
/* B without C has no output. */
fold.output_line = y;
fold.tree = 1;
/* Block is collapsed? */
fold.collapsed = data->source_flags[y] &
WINDOW_COPY_FOLD_COLLAPSED;
if (fold.prompt_dy != UINT_MAX)
/*
* Make this a +/- control row
* but without the +/- for now
* since we don't know what
* comes below.
*/
/* Show output controls. */
window_copy_set_control(data,
fold.prompt_dy,
fold.output_line, 0);
}
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,
fold.prompt_dy, osc133->exit_status);
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.
*/
/* Keep the initial fold state. */
fold.hiding = data->source_flags[0] &
WINDOW_COPY_FOLD_COLLAPSED;
else
/*
* Otherwise, use the current block's
* fold state.
*/
fold.hiding = fold.collapsed;
fold.command = 0;
}
/*
* Process osc133 C and D before a following prompt
* at the same column.
*/
/* Process C and D before A at the same column. */
if ((sgl->flags & GRID_LINE_START_PROMPT) &&
x == osc133->prompt_col) {
/* osc133 A ends region, starts new prompt. */
/* A starts a new prompt. */
dgl->flags |= GRID_LINE_START_PROMPT;
dgl->osc133_data.prompt_col = dst->cx;
fold.prompt_dy = dy;
@@ -922,21 +873,16 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
}
if ((sgl->flags & GRID_LINE_SECOND_PROMPT) &&
x == osc133->prompt_col) {
/* osc133 A;k=s, marks secondary prompt. */
dgl->flags |= GRID_LINE_SECOND_PROMPT;
dgl->osc133_data.prompt_col = dst->cx;
}
if ((sgl->flags & GRID_LINE_START_COMMAND) &&
x == osc133->cmd_col) {
/* osc133 B marks start/continue of command. */
dgl->flags |= GRID_LINE_START_COMMAND;
dgl->osc133_data.cmd_col = dst->cx;
fold.command = 1;
}
/*
* An osc133 A may expose the remainder of this source
* line after a folded region.
*/
/* A may expose cells after a folded region. */
if (was_hiding && !fold.hiding)
window_copy_set_line(data, dy, y,
fold.output_line, 0);
@@ -952,40 +898,31 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
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.
*/
/* Skip hidden cells after processing their markers. */
if (fold.hiding)
continue;
if (!(gc.flags & GRID_FLAG_PADDING)) {
screen_write_cell(&ctx, &gc);
if (fold.tree)
window_copy_set_line(data, dy, y,
fold.output_line, WINDOW_COPY_LINE_MEMBER);
fold.output_line,
WINDOW_COPY_LINE_MEMBER);
}
}
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 */
/* Continue the gutter tree on empty lines. */
window_copy_set_line(data, dy, y, fold.output_line,
WINDOW_COPY_LINE_MEMBER);
if (y + 1 != total && !(sgl->flags & GRID_LINE_WRAPPED) &&
(!fold.hiding || (fold.initial && y == 0))) {
/*
* Hide folded line breaks, except after the initial
* synthetic command line (top line of copy buffer).
*/
/* Keep the initial region's control line visible. */
screen_write_carriagereturn(&ctx);
screen_write_linefeed(&ctx, 0, 8);
}
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.
*/
/* Fold initial output below its control line. */
fold.tree = 1;
fold.hiding = data->source_flags[0] &
WINDOW_COPY_FOLD_COLLAPSED;
@@ -1029,11 +966,10 @@ window_copy_sync_backing(struct window_mode_entry *wme)
struct screen *src = &wp->base;
struct screen *dst = data->source;
struct grid *sg = src->grid;
struct grid *dg = dst->grid;
u_int sy = sg->sy;
u_int old_hsize = dg->hsize;
u_int new_hsize = sg->hsize;
u_int added, collected, kept, old_count, new_count;
struct grid *dg;
u_int sy, old_hsize, new_hsize;
u_int added, collected, kept;
u_int old_count, new_count;
/*
* Only a pane's own live grid is tracked incrementally. A different
@@ -1042,6 +978,10 @@ window_copy_sync_backing(struct window_mode_entry *wme)
*/
if (data->source == NULL || data->viewmode || wme->swp != wme->wp)
return (0);
dg = dst->grid;
sy = sg->sy;
old_hsize = dg->hsize;
new_hsize = sg->hsize;
/* Indices only line up at the same size and generation. */
if (sg->sx != dg->sx || sg->sy != dg->sy ||
@@ -1064,17 +1004,15 @@ window_copy_sync_backing(struct window_mode_entry *wme)
kept = old_hsize - collected;
old_count = old_hsize + sy;
new_count = new_hsize + sy;
if (collected != 0) {
if (collected != 0)
memmove(data->source_flags, data->source_flags + collected,
(old_count - collected) * sizeof *data->source_flags);
}
data->source_flags = xreallocarray(data->source_flags, new_count,
sizeof *data->source_flags);
if (new_count > old_count - collected) {
if (new_count > old_count - collected)
memset(data->source_flags + old_count - collected, 0,
(new_count - (old_count - collected)) *
sizeof *data->source_flags);
}
data->source_line_count = new_count;
if (added == 0 && collected == 0) {
@@ -1185,7 +1123,7 @@ window_copy_init(struct window_mode_entry *wme,
screen_size_y(data->source);
data->fold_view = args_has(args, 'c') || args_has(args, 'U');
if (args_has(args, 'c'))
window_copy_set_all_folds(data, 1);
window_copy_set_all_folds(data, 1);
window_copy_rebuild_backing(wme);
window_copy_sync_snapshot(data, base->grid);
@@ -1194,7 +1132,8 @@ window_copy_init(struct window_mode_entry *wme,
window_copy_find_fold(wme, cy, &prompt, &chosen)) {
restored = window_copy_restore_cursor(wme, cx, cy);
if (!restored)
restored = window_copy_restore_command_end(wme, prompt, chosen);
restored = window_copy_restore_command_end(wme, prompt,
chosen);
} else if (data->fold_view)
restored = window_copy_restore_cursor(wme, cx, cy);
if (!restored && cy < screen_hsize(data->backing)) {
@@ -2183,10 +2122,11 @@ 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 = {
struct window_copy_fold_request request = {
.action = WINDOW_COPY_FOLD_EXPAND
};
struct grid_reader gr;
struct grid *gd;
void *buf;
size_t len;
u_int sx, sy, ex, ey, bsx, bsy, bex, bey;
@@ -2194,10 +2134,13 @@ window_copy_cmd_select_output(struct window_copy_cmd_state *cs)
int all = args_has(cs->wargs, 'a');
if (all) {
gd = data->backing->grid;
if (data->source != NULL)
gd = data->source->grid;
sx = sy = 0;
total = data->source->grid->hsize + data->source->grid->sy;
total = gd->hsize + gd->sy;
ey = total - 1;
ex = grid_get_line(data->source->grid, ey)->cellused;
ex = grid_get_line(gd, ey)->cellused;
} else if (!window_copy_find_output_range(wme, &sx, &sy, &ex, &ey))
return (WINDOW_COPY_CMD_NOTHING);
buf = window_copy_get_output(wme, &len, all);
@@ -4145,8 +4088,8 @@ window_copy_find_fold(struct window_mode_entry *wme, u_int target,
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) {
if (have_prompt && candidate != UINT_MAX &&
target >= last_prompt && target < y) {
*prompt = last_prompt;
*chosen = candidate;
return (1);
@@ -4194,8 +4137,7 @@ window_copy_cursor_source(struct window_mode_entry *wme, u_int *source_x,
else if (line->source_line == UINT_MAX) {
*source_x = UINT_MAX;
*source_y = data->source_line_count - 1;
}
else
} else
*source_y = line->source_line;
}
@@ -4263,7 +4205,8 @@ window_copy_restore_command_end(struct window_mode_entry *wme, u_int prompt,
struct grid *gd = data->backing->grid;
struct grid_line *gl;
struct window_copy_line *line;
u_int command_start = UINT_MAX, marker = UINT_MAX;
u_int command_start = UINT_MAX;
u_int marker = UINT_MAX;
u_int marker_x = 0, length, y;
int previous = 0;
@@ -4354,9 +4297,10 @@ window_copy_set_fold(struct window_mode_entry *wme,
{
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 chosen = UINT_MAX, first_prompt;
u_int target = request->target;
int changed = 0, enabled = 0, found, initial;
int changed = 0, enabled = 0;
int found, initial;
int wanted;
if (data->source == NULL)
@@ -4364,44 +4308,30 @@ window_copy_set_fold(struct window_mode_entry *wme,
window_copy_cursor_source(wme, &source_x, &source_y);
if (target == UINT_MAX)
target = source_y;
first_prompt = screen_hsize(data->source) + screen_size_y(data->source);
for (y = 0; y < first_prompt; y++) {
if (grid_get_line(data->source->grid, y)->flags &
GRID_LINE_START_PROMPT) {
first_prompt = y;
break;
}
}
initial = first_prompt != 0 &&
first_prompt != screen_hsize(data->source) +
screen_size_y(data->source) && target < first_prompt;
first_prompt = window_copy_first_prompt(data->source->grid);
initial = (first_prompt != 0 && first_prompt != UINT_MAX &&
target < first_prompt);
if (!data->fold_view) {
data->fold_view = 1;
window_copy_rebuild_backing(wme);
enabled = 1;
}
found = window_copy_find_fold(wme, target, &prompt, &chosen);
wanted = (request->action == WINDOW_COPY_FOLD_COLLAPSE);
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 = (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;
}
changed = window_copy_set_source_folded(data, 0, wanted);
} else if (found && chosen != UINT_MAX) {
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)) {
changed = 1;
}
changed = window_copy_set_source_folded(data, chosen, wanted);
}
if (!changed) {
if (enabled) {
@@ -4431,7 +4361,7 @@ window_copy_set_fold(struct window_mode_entry *wme,
static enum window_copy_cmd_action
window_copy_cmd_collapse_output(struct window_copy_cmd_state *cs)
{
struct window_copy_fold_request request = {
struct window_copy_fold_request request = {
.action = WINDOW_COPY_FOLD_COLLAPSE,
.all = args_has(cs->wargs, 'a'),
.target = UINT_MAX
@@ -4445,7 +4375,7 @@ 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)
{
struct window_copy_fold_request request = {
struct window_copy_fold_request request = {
.action = WINDOW_COPY_FOLD_EXPAND,
.all = args_has(cs->wargs, 'a'),
.target = UINT_MAX
@@ -4461,7 +4391,7 @@ 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 = {
struct window_copy_fold_request request = {
.action = WINDOW_COPY_FOLD_TOGGLE,
.all = args_has(cs->wargs, 'a'),
.target = UINT_MAX
@@ -4469,26 +4399,31 @@ window_copy_cmd_toggle_output(struct window_copy_cmd_state *cs)
u_int current, current_prompt, target;
u_int target_prompt, chosen, x, y;
int current_found;
int initial, target_found;
int initial;
if (args_has(cs->wargs, 'm')) {
if (cs->m == NULL || cmd_mouse_at(cs->wme->wp, cs->m, &x, &y,
0) != 0 || x >= window_copy_left_margin(cs->wme))
if (cs->m == NULL)
return (WINDOW_COPY_CMD_NOTHING);
if (cmd_mouse_at(cs->wme->wp, cs->m, &x, &y, 0) != 0)
return (WINDOW_COPY_CMD_NOTHING);
if (x >= window_copy_left_margin(cs->wme))
return (WINDOW_COPY_CMD_NOTHING);
target = screen_hsize(data->backing) + y - data->oy;
line = window_copy_get_line_info(data, target);
if (line == NULL || line->source_line == UINT_MAX)
return (WINDOW_COPY_CMD_NOTHING);
initial = line->flags & WINDOW_COPY_LINE_INITIAL;
window_copy_cursor_source(cs->wme, &x, &current);
current_found = window_copy_find_fold(cs->wme, current,
&current_prompt, &chosen);
target_found = initial || window_copy_find_fold(cs->wme,
line->source_line, &target_prompt, &chosen);
if (!target_found)
return (WINDOW_COPY_CMD_NOTHING);
request.command_end = initial || !current_found ||
current_prompt != target_prompt;
request.command_end = 1;
if (!initial) {
if (!window_copy_find_fold(cs->wme, line->source_line,
&target_prompt, &chosen))
return (WINDOW_COPY_CMD_NOTHING);
window_copy_cursor_source(cs->wme, &x, &current);
current_found = window_copy_find_fold(cs->wme, current,
&current_prompt, &chosen);
request.command_end = !current_found ||
current_prompt != target_prompt;
}
request.target = line->source_line;
}
if (window_copy_set_fold(cs->wme, &request))
@@ -5178,7 +5113,8 @@ window_copy_command(struct window_mode_entry *wme, struct client *c,
enum window_copy_cmd_action action;
enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER;
const char *command;
u_int i, count = args_count(args), mouse_x, mouse_y;
u_int i, count = args_count(args);
u_int mouse_x, mouse_y;
int keys, flags, mouse_control = 0;
char *error = NULL;
@@ -6606,12 +6542,12 @@ static u_int
window_copy_left_margin(struct window_mode_entry *wme)
{
struct window_copy_mode_data *data = wme->data;
u_int width = 0;
u_int width;
width = window_copy_line_number_width(wme);
if (data->fold_view)
width = data->output_status_width + 2;
return (window_copy_line_number_width(wme) + width);
width += data->output_status_width + 2;
return (width);
}
static u_int
@@ -6702,6 +6638,61 @@ window_copy_get_current_offset(struct window_pane *wp, u_int *offset,
return (1);
}
static void
window_copy_write_gutter(struct window_mode_entry *wme,
struct screen_write_ctx *ctx, u_int py, u_int line_width,
const struct grid_cell *gc)
{
struct window_copy_mode_data *data = wme->data;
struct window_copy_line *line, *next;
struct grid_cell control_gc;
u_int backing_y, output_line = UINT_MAX, i;
u_int width = data->output_status_width + 2;
char control = ' ', *status;
backing_y = screen_hsize(data->backing) - data->oy + py;
line = window_copy_get_line_info(data, backing_y);
if (line != NULL && line->flags & WINDOW_COPY_LINE_CONTROL) {
if (line->flags & WINDOW_COPY_LINE_INITIAL)
output_line = 0;
else if (line->flags & WINDOW_COPY_LINE_EXPANDABLE &&
line->output_line < data->source_line_count)
output_line = line->output_line;
if (output_line != UINT_MAX) {
if (data->source_flags[output_line] &
WINDOW_COPY_FOLD_COLLAPSED)
control = '+';
else
control = '-';
}
} else if (line != NULL && line->flags & WINDOW_COPY_LINE_MEMBER) {
next = window_copy_get_line_info(data, backing_y + 1);
if (next == NULL || ~next->flags & WINDOW_COPY_LINE_MEMBER ||
next->output_line != line->output_line)
control = 'm'; /* ACS L character */
else
control = 'x'; /* ACS | character */
}
screen_write_cursormove(ctx, line_width, py, 0);
for (i = 0; i < width; i++)
screen_write_putc(ctx, gc, ' ');
if (control != ' ') {
screen_write_cursormove(ctx,
line_width + data->output_status_width, py, 0);
memcpy(&control_gc, gc, sizeof control_gc);
if (control == 'x' || control == 'm')
control_gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &control_gc, control);
}
if (line != NULL && line->flags & WINDOW_COPY_LINE_CONTROL) {
status = window_copy_expand_exit_status(wme, line);
screen_write_cursormove(ctx, line_width, py, 0);
format_draw(ctx, gc, data->output_status_width, status, NULL,
0);
free(status);
}
}
static void
window_copy_write_line(struct window_mode_entry *wme,
struct screen_write_ctx *ctx, u_int py)
@@ -6712,22 +6703,18 @@ window_copy_write_line(struct window_mode_entry *wme,
struct options *oo = wp->window->options;
struct grid_cell gc, mgc, cgc, mkgc, clgc, ln_gc;
struct grid_cell cur_ln_gc;
struct grid_cell control_gc;
struct window_copy_line *line, *next;
const struct grid_cell *line_gc;
u_int sx = screen_size_x(s);
u_int hsize = screen_hsize(data->backing);
u_int width, line_width, gutter_width, backing_y;
u_int width, line_width, backing_y;
u_int absolute, line_number, content_sx;
u_int i;
const char *value;
char *expanded, *status;
char *expanded;
struct format_tree *ft;
char control;
int current, mode;
line_width = window_copy_line_number_width(wme);
gutter_width = data->fold_view ? data->output_status_width + 2 : 0;
width = line_width + gutter_width;
width = window_copy_left_margin(wme);
if (width >= sx)
content_sx = 1;
else if (width != 0)
@@ -6755,6 +6742,7 @@ window_copy_write_line(struct window_mode_entry *wme,
"copy-mode-current-line-number-style", ft);
cur_ln_gc.flags |= GRID_FLAG_NOPALETTE;
current = (py == data->cy);
line_gc = current ? &cur_ln_gc : &ln_gc;
backing_y = hsize - data->oy + py;
absolute = backing_y + 1;
if (data->lines != NULL && backing_y < data->line_count &&
@@ -6780,62 +6768,12 @@ window_copy_write_line(struct window_mode_entry *wme,
line_number = data->cy - py;
if (line_width != 0) {
screen_write_cursormove(ctx, 0, py, 0);
screen_write_nputs(ctx, line_width,
current ? &cur_ln_gc : &ln_gc, "%*u ",
screen_write_nputs(ctx, line_width, line_gc, "%*u ",
(int)line_width - 1, line_number);
}
if (gutter_width != 0) {
line = NULL;
control = ' ';
if (data->lines != NULL && backing_y < data->line_count)
line = &data->lines[backing_y];
if (line != NULL &&
line->flags & WINDOW_COPY_LINE_CONTROL) {
if (line->flags &
WINDOW_COPY_LINE_INITIAL) {
control = data->source_flags[0] &
WINDOW_COPY_FOLD_COLLAPSED ? '+' : '-';
} else if (line->flags & WINDOW_COPY_LINE_EXPANDABLE &&
line->output_line < data->source_line_count) {
if (data->source_flags[line->output_line] &
WINDOW_COPY_FOLD_COLLAPSED)
control = '+';
else
control = '-';
}
} else if (line != NULL &&
line->flags & WINDOW_COPY_LINE_MEMBER) {
next = NULL;
if (backing_y + 1 < data->line_count)
next = &data->lines[backing_y + 1];
if (next == NULL ||
~(next->flags) & WINDOW_COPY_LINE_MEMBER ||
next->output_line != line->output_line)
control = 'm'; /* ACS L character */
else
control = 'x'; /* ACS | character */
}
screen_write_cursormove(ctx, line_width, py, 0);
for (i = 0; i < gutter_width; i++)
screen_write_putc(ctx, current ? &cur_ln_gc : &ln_gc, ' ');
if (control != ' ') {
screen_write_cursormove(ctx,
line_width + data->output_status_width, py, 0);
memcpy(&control_gc, current ? &cur_ln_gc : &ln_gc,
sizeof control_gc);
if (control == 'x' || control == 'm')
control_gc.attr |= GRID_ATTR_CHARSET;
screen_write_putc(ctx, &control_gc, control);
}
if (line != NULL &&
line->flags & WINDOW_COPY_LINE_CONTROL) {
status = window_copy_expand_exit_status(wme, line);
screen_write_cursormove(ctx, line_width, py, 0);
format_draw(ctx, current ? &cur_ln_gc : &ln_gc,
data->output_status_width, status, NULL, 0);
free(status);
}
}
if (data->fold_view)
window_copy_write_gutter(wme, ctx, py, line_width,
line_gc);
}
window_copy_write_one(wme, ctx, width, py, hsize - data->oy + py,
@@ -6847,7 +6785,8 @@ window_copy_write_line(struct window_mode_entry *wme,
expanded = format_expand(ft, value);
if (*expanded != '\0') {
screen_write_cursormove(ctx, width, 0, 0);
format_draw(ctx, &gc, content_sx, expanded, NULL, 0);
format_draw(ctx, &gc, content_sx, expanded,
NULL, 0);
}
free(expanded);
}
@@ -7456,7 +7395,8 @@ window_copy_find_previous_output_range(struct screen *s, u_int cursor_x,
struct osc133_data *od;
u_int start_x, start_y, end_x, end_y;
u_int y, total;
int found = 0, found_empty = 0, have_prompt = 0;
int found = 0, found_empty = 0;
int have_prompt = 0;
int pending = 0, started_here;
int has_start, has_end, end_first;
@@ -7491,12 +7431,7 @@ window_copy_find_previous_output_range(struct screen *s, u_int cursor_x,
end_x = od->out_end_col;
end_y = y;
/*
* Prefer a previously found non-empty output over a
* later one that turns out to be empty (for example a
* command with no output immediately followed by the
* cursor's prompt).
*/
/* Prefer earlier output to a later empty command. */
if (!found || found_empty ||
start_x != end_x || start_y != end_y) {
*sx = start_x;
@@ -7538,13 +7473,12 @@ window_copy_get_output(struct window_mode_entry *wme, size_t *len, int all)
{
struct window_copy_mode_data *data = wme->data;
struct grid *gd;
void *buf;
u_int sx, sy, ex, ey, total;
if (data->source == NULL) {
*len = 0;
return (NULL);
}
gd = data->source->grid;
gd = data->backing->grid;
if (data->source != NULL)
gd = data->source->grid;
total = gd->hsize + gd->sy;
if (all) {
sx = sy = 0;
@@ -7554,7 +7488,8 @@ window_copy_get_output(struct window_mode_entry *wme, size_t *len, int all)
*len = 0;
return (NULL);
}
return (window_copy_get_grid_range(gd, sx, sy, ex, ey, len));
buf = window_copy_get_grid_range(gd, sx, sy, ex, ey, len);
return (buf);
}
static void *