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

30
input.c
View File

@@ -3199,6 +3199,7 @@ input_osc_133_save_marker(struct window_pane *wp, struct screen *s)
{ {
struct grid *gd = s->grid; struct grid *gd = s->grid;
struct grid_line *gl; struct grid_line *gl;
struct osc133_data *od;
struct input_osc_133_ctx ctx = {0}; struct input_osc_133_ctx ctx = {0};
u_int y; u_int y;
@@ -3210,16 +3211,21 @@ input_osc_133_save_marker(struct window_pane *wp, struct screen *s)
ctx.collected = gd->scroll_collected; ctx.collected = gd->scroll_collected;
for (y = gd->hsize + gd->sy; y > 0; y--) { for (y = gd->hsize + gd->sy; y > 0; y--) {
gl = grid_get_line(gd, y - 1); gl = grid_get_line(gd, y - 1);
if ((gl->flags & GRID_LINE_START_OUTPUT) && if (~gl->flags & GRID_LINE_START_OUTPUT) {
(~gl->flags & GRID_LINE_END_OUTPUT || if (gl->flags &
gl->osc133_data.out_end_col < gl->osc133_data.out_start_col) && (GRID_LINE_START_PROMPT|GRID_LINE_END_OUTPUT))
(~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; 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; break;
} }
return (ctx); return (ctx);
@@ -3252,9 +3258,8 @@ input_osc_133_restore_marker(struct screen *s,
static int static int
input_osc_133_exit_status(const char *p, int *present) input_osc_133_exit_status(const char *p, int *present)
{ {
const char *end; const char *end, *errstr;
char *copy; char *copy;
char *endptr;
long long status; long long status;
*present = 0; *present = 0;
@@ -3272,10 +3277,9 @@ input_osc_133_exit_status(const char *p, int *present)
return (0); return (0);
} }
*present = 1; *present = 1;
errno = 0; status = strtonum(copy, 0, 255, &errstr);
status = (int)strtol(copy, &endptr, 10);
free(copy); free(copy);
if (errno != 0 || endptr == copy || status < 0 || status > 255) if (errstr != NULL)
return (255); return (255);
return (status); return (status);
} }

View File

@@ -587,7 +587,7 @@ key_bindings_init(void)
"bind -Tcopy-mode C-[ { send -X cancel }", "bind -Tcopy-mode C-[ { send -X cancel }",
"bind -Tcopy-mode Space { send -X page-down }", "bind -Tcopy-mode Space { send -X page-down }",
"bind -Tcopy-mode Tab { send -X toggle-output }", "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-reverse }",
"bind -Tcopy-mode \\; { send -X jump-again }", "bind -Tcopy-mode \\; { send -X jump-again }",
"bind -Tcopy-mode F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }", "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 C-[ { send -X clear-selection }",
"bind -Tcopy-mode-vi Space { send -X begin-selection }", "bind -Tcopy-mode-vi Space { send -X begin-selection }",
"bind -Tcopy-mode-vi Tab { send -X toggle-output }", "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 end-of-line }",
"bind -Tcopy-mode-vi , { send -X jump-reverse }", "bind -Tcopy-mode-vi , { send -X jump-reverse }",
"bind -Tcopy-mode-vi / { command-prompt -P -T search -p'(search down)' { send -X search-forward -- '%%' } }", "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[] = { static const char *options_table_copy_mode_line_numbers_list[] = {
"off", "default", "absolute", "relative", "hybrid", NULL "off", "default", "absolute", "relative", "hybrid", NULL
}; };
/* Status line format. */ /* Status line format. */
#define OPTIONS_TABLE_STATUS_FORMAT1 \ #define OPTIONS_TABLE_STATUS_FORMAT1 \
"#[align=left range=left #{E:status-left-style}]" \ "#[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. # A format wider than the standard three-column gutter is not truncated.
$TMUX2 send -X cancel || exit 1 $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 '#[align=right]#{?exit_status,!#{exit_status}, }' || exit 1
$TMUX2 copy-mode -c || exit 1 $TMUX2 copy-mode -c || exit 1
$TMUX2 send -X history-top || 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 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' '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 $TMUX kill-server 2>/dev/null
exit $exit_status exit $exit_status

View File

@@ -288,4 +288,18 @@ $TMUX show-buffer >$OUT
grep -q seq "$OUT" && exit 1 grep -q seq "$OUT" && exit 1
$TMUX send-keys -t :hist.0 -X cancel || 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 exit 0

16
tmux.1
View File

@@ -2213,10 +2213,23 @@ Expand the output for the command on the cursor line.
With With
.Fl a , .Fl a ,
expand all command output in the copy-mode buffer. 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 The Tab key toggles output for the command on the cursor line and S-Tab
toggles all command output. toggles all command output.
.It Xo .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 .Ic middle\-line
(vi: M) (vi: M)
(emacs: M\-r) (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_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_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_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_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" Ta "" Ta "Position shown in the copy mode indicator"
.It Li "copy_position_limit" Ta "" Ta "Limit 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); struct screen_write_ctx *, u_int);
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_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 void window_copy_rebuild_backing(struct window_mode_entry *);
static int window_copy_set_fold(struct window_mode_entry *, static int window_copy_set_fold(struct window_mode_entry *,
const struct window_copy_fold_request *); const struct window_copy_fold_request *);
@@ -339,7 +342,7 @@ struct window_copy_mode_data {
struct screen *backing; struct screen *backing;
struct screen *source; struct screen *source;
u_char *source_flags; /* Flags per source line */ u_char *source_flags; /* flags per source line */
u_int source_line_count; u_int source_line_count;
struct window_copy_line *lines; struct window_copy_line *lines;
u_int line_count; u_int line_count;
@@ -560,6 +563,8 @@ window_copy_set_control(struct window_copy_mode_data *data, u_int y,
line->flags |= WINDOW_COPY_LINE_CONTROL; line->flags |= WINDOW_COPY_LINE_CONTROL;
if (expandable) if (expandable)
line->flags |= WINDOW_COPY_LINE_EXPANDABLE; line->flags |= WINDOW_COPY_LINE_EXPANDABLE;
else
line->flags &= ~WINDOW_COPY_LINE_EXPANDABLE;
} }
static void static void
@@ -583,10 +588,7 @@ window_copy_expand_exit_status(struct window_mode_entry *wme,
const char *value; const char *value;
char *expanded; char *expanded;
/* /* The copy cursor and viewport have not been restored yet. */
* This is called while rebuilding the backing grid, before the copy
* cursor and viewport have been restored for the new grid.
*/
ft = format_create(NULL, NULL, 0, 0); ft = format_create(NULL, NULL, 0, 0);
format_add(ft, "exit_status", "0"); format_add(ft, "exit_status", "0");
format_add(ft, "exit_status_present", "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", "%hhu", line->exit_status);
format_add(ft, "exit_status_present", "1"); 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"); "copy-mode-exit-status-format");
expanded = format_expand(ft, value); expanded = format_expand(ft, value);
format_free(ft); 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_mode_data *data = wme->data;
struct window_copy_line *line; struct window_copy_line *line;
char *expanded; char *expanded;
u_int width, y; u_int width, length, y;
data->output_status_width = 0; data->output_status_width = 0;
if (!data->fold_view || data->lines == NULL) 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) if (~line->flags & WINDOW_COPY_LINE_CONTROL)
continue; continue;
expanded = window_copy_expand_exit_status(wme, line); expanded = window_copy_expand_exit_status(wme, line);
if (format_width(expanded) > width) length = format_width(expanded);
width = format_width(expanded); if (length > width)
width = length;
free(expanded); free(expanded);
} }
data->output_status_width = width; 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); 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 static int
window_copy_any_fold_collapsed(struct window_copy_mode_data *data) 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; u_int y;
int command = 0, output = 0; int command = 0, output = 0;
for (y = 0; y < gd->hsize + gd->sy; y++) { y = window_copy_first_prompt(gd);
gl = grid_get_line(gd, y); if (y != 0 && y != UINT_MAX &&
if (gl->flags & GRID_LINE_START_PROMPT)
break;
}
if (y != 0 && y != gd->hsize + gd->sy &&
(data->source_flags[0] & WINDOW_COPY_FOLD_COLLAPSED)) (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); return (1);
for (y = 0; y < gd->hsize + gd->sy; y++) { 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 (gl->flags & GRID_LINE_END_OUTPUT) {
if (!output && command && if (!output && command &&
(data->source_flags[y] & WINDOW_COPY_FOLD_COLLAPSED)) (data->source_flags[y] &
WINDOW_COPY_FOLD_COLLAPSED))
return (1); return (1);
command = output = 0; command = output = 0;
} }
@@ -687,15 +700,10 @@ window_copy_set_all_folds(struct window_copy_mode_data *data, int collapse)
u_int y; u_int y;
int changed = 0, command = 0, output = 0; int changed = 0, command = 0, output = 0;
for (y = 0; y < gd->hsize + gd->sy; y++) { y = window_copy_first_prompt(gd);
gl = grid_get_line(gd, y); if (y != 0 && y != UINT_MAX &&
if (gl->flags & GRID_LINE_START_PROMPT) window_copy_set_source_folded(data, 0, collapse))
break;
}
if (y != 0 && y != gd->hsize + gd->sy &&
window_copy_set_source_folded(data, 0, collapse)) {
changed = 1; changed = 1;
}
for (y = 0; y < gd->hsize + gd->sy; y++) { for (y = 0; y < gd->hsize + gd->sy; y++) {
gl = grid_get_line(gd, 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) if (gl->flags & GRID_LINE_START_COMMAND)
command = 1; command = 1;
if (gl->flags & GRID_LINE_START_OUTPUT) { 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; changed = 1;
}
output = 1; output = 1;
} }
if (gl->flags & GRID_LINE_END_OUTPUT) { if (gl->flags & GRID_LINE_END_OUTPUT) {
if (!output && command && if (!output && command &&
window_copy_set_source_folded(data, y, collapse)) { window_copy_set_source_folded(data, y, collapse))
changed = 1; changed = 1;
}
command = output = 0; command = output = 0;
} }
} }
@@ -730,7 +736,6 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
struct screen *src = data->source, *dst; struct screen *src = data->source, *dst;
struct grid *sgd; struct grid *sgd;
struct grid_line *sgl, *dgl; struct grid_line *sgl, *dgl;
struct window_copy_line *line;
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;
@@ -744,7 +749,8 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
if (src == NULL) if (src == NULL)
return; return;
if (!data->fold_view) { 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) { if (data->backing != NULL) {
screen_free(data->backing); screen_free(data->backing);
free(data->backing); free(data->backing);
@@ -759,8 +765,8 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
sgd = src->grid; sgd = src->grid;
total = sgd->hsize + sgd->sy; total = sgd->hsize + sgd->sy;
dst = xcalloc(1, sizeof *dst); dst = xcalloc(1, sizeof *dst);
screen_init(dst, screen_size_x(&data->screen), screen_size_y(&data->screen), screen_init(dst, screen_size_x(&data->screen),
screen_hlimit(src)); screen_size_y(&data->screen), screen_hlimit(src));
dst->grid->flags |= GRID_HISTORY; dst->grid->flags |= GRID_HISTORY;
free(data->lines); free(data->lines);
data->lines = xcalloc(total, sizeof *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].source_line = UINT_MAX;
data->lines[y].output_line = UINT_MAX; data->lines[y].output_line = UINT_MAX;
} }
for (y = 0; y < total; y++) { y = window_copy_first_prompt(sgd);
sgl = grid_get_line(sgd, y); /* Keep the first line as the control for any initial output. */
if (sgl->flags & GRID_LINE_START_PROMPT) fold.initial = (y != 0 && y != UINT_MAX);
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;
screen_write_start(&ctx, dst); screen_write_start(&ctx, dst);
/* /* Include the final cell boundary for markers after the last cell. */
* 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.
*/
for (y = 0; y < total; y++) { for (y = 0; y < total; y++) {
sgl = grid_get_line(sgd, y); sgl = grid_get_line(sgd, y);
osc133 = &sgl->osc133_data; osc133 = &sgl->osc133_data;
@@ -794,124 +788,81 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
dy = dst->grid->hsize + dst->cy; dy = dst->grid->hsize + dst->cy;
was_hiding = fold.hiding; was_hiding = fold.hiding;
if (!fold.hiding) if (!fold.hiding)
/* /* Map visible rows to their source. */
* 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, window_copy_set_line(data, dy, y,
fold.output_line, 0); fold.output_line, 0);
/* /* Process markers on hidden rows too. */
* Continue even if row is folded because
* there may be markers to process.
*/
dgl = grid_get_line(dst->grid, dy); dgl = grid_get_line(dst->grid, dy);
if (fold.initial && y == 0 && x == 0) if (fold.initial && y == 0 && x == 0)
/* Top line of buffer, no osc133 marker yet. /* Attach initial output controls. */
* Attach controls to top line, make it a
* "synthetic command" so top lines can fold.
*/
window_copy_set_line(data, dy, y, UINT_MAX, 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) && if ((sgl->flags & GRID_LINE_START_OUTPUT) &&
x == osc133->out_start_col) { x == osc133->out_start_col) {
/* /* C starts the collapsible output region. */
* osc133 C starts the collapsible region
* controlled by this source line.
*/
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;
fold.output_line = y; fold.output_line = y;
/* used to draw the | or L in gutter */
fold.tree = 1; fold.tree = 1;
fold.in_output = 1; fold.in_output = 1;
fold.output_empty = 1; fold.output_empty = 1;
fold.collapsed = data->source_flags[y] & fold.collapsed = data->source_flags[y] &
WINDOW_COPY_FOLD_COLLAPSED; WINDOW_COPY_FOLD_COLLAPSED;
/* Start/stop hiding cells */
fold.hiding = fold.collapsed; fold.hiding = fold.collapsed;
if (fold.prompt_dy != UINT_MAX) if (fold.prompt_dy != UINT_MAX)
/* Attach +/- controls to the prompt. */ /* Attach +/- controls to the prompt. */
window_copy_set_control(data, 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) && if ((sgl->flags & GRID_LINE_END_OUTPUT) &&
x == osc133->out_end_col) { x == osc133->out_end_col) {
/* /* Fold text after D until the next A. */
* 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.
*/
dgl->flags |= GRID_LINE_END_OUTPUT; dgl->flags |= GRID_LINE_END_OUTPUT;
if (sgl->flags & GRID_LINE_END_OUTPUT_STATUS) 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.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 && if (fold.in_output && fold.output_empty &&
fold.prompt_dy != UINT_MAX) { fold.prompt_dy != UINT_MAX)
line = window_copy_get_line_info(data,
fold.prompt_dy);
if (line != NULL)
/* No output to hide, no +/-. */ /* No output to hide, no +/-. */
line->flags &= ~WINDOW_COPY_LINE_EXPANDABLE; window_copy_set_control(data,
} fold.prompt_dy,
fold.output_line, 0);
fold.in_output = 0; fold.in_output = 0;
if (fold.output_line == UINT_MAX && if (fold.output_line == UINT_MAX &&
fold.command) { fold.command) {
/* /* B without C has no output. */
* Saw osc133 B but not C marker.
* This could be user pressed enter
* or command like 'true' with no
* output.
*/
fold.output_line = y; fold.output_line = y;
fold.tree = 1; fold.tree = 1;
/* Block is collapsed? */
fold.collapsed = data->source_flags[y] & fold.collapsed = data->source_flags[y] &
WINDOW_COPY_FOLD_COLLAPSED; WINDOW_COPY_FOLD_COLLAPSED;
if (fold.prompt_dy != UINT_MAX) if (fold.prompt_dy != UINT_MAX)
/* /* Show output controls. */
* Make this a +/- control row
* but without the +/- for now
* since we don't know what
* comes below.
*/
window_copy_set_control(data, window_copy_set_control(data,
fold.prompt_dy, fold.prompt_dy,
fold.output_line, 0); fold.output_line, 0);
} }
if (fold.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
* command's prompt row.
*/
window_copy_set_exit_status(data, 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) if (fold.initial && fold.prompt_dy == UINT_MAX)
/* At top of buffer before first osc A /* Keep the initial fold state. */
* marker use first line's fold state.
*/
fold.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
* fold state.
*/
fold.hiding = fold.collapsed; fold.hiding = fold.collapsed;
fold.command = 0; fold.command = 0;
} }
/* /* Process C and D before A at the same column. */
* Process osc133 C and D before a following prompt
* at the same column.
*/
if ((sgl->flags & GRID_LINE_START_PROMPT) && if ((sgl->flags & GRID_LINE_START_PROMPT) &&
x == osc133->prompt_col) { x == osc133->prompt_col) {
/* osc133 A ends region, starts new prompt. */ /* A starts a 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;
fold.prompt_dy = dy; fold.prompt_dy = dy;
@@ -922,21 +873,16 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
} }
if ((sgl->flags & GRID_LINE_SECOND_PROMPT) && if ((sgl->flags & GRID_LINE_SECOND_PROMPT) &&
x == osc133->prompt_col) { x == osc133->prompt_col) {
/* osc133 A;k=s, marks secondary prompt. */
dgl->flags |= GRID_LINE_SECOND_PROMPT; dgl->flags |= GRID_LINE_SECOND_PROMPT;
dgl->osc133_data.prompt_col = dst->cx; dgl->osc133_data.prompt_col = dst->cx;
} }
if ((sgl->flags & GRID_LINE_START_COMMAND) && if ((sgl->flags & GRID_LINE_START_COMMAND) &&
x == osc133->cmd_col) { x == osc133->cmd_col) {
/* 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;
fold.command = 1; fold.command = 1;
} }
/* /* A may expose cells after a folded region. */
* An osc133 A may expose the remainder of this source
* line after a folded region.
*/
if (was_hiding && !fold.hiding) if (was_hiding && !fold.hiding)
window_copy_set_line(data, dy, y, window_copy_set_line(data, dy, y,
fold.output_line, 0); fold.output_line, 0);
@@ -952,40 +898,31 @@ window_copy_rebuild_backing(struct window_mode_entry *wme)
fold.prompt_dy != UINT_MAX) fold.prompt_dy != UINT_MAX)
window_copy_set_control(data, fold.prompt_dy, window_copy_set_control(data, fold.prompt_dy,
fold.output_line, 1); fold.output_line, 1);
/* /* Skip hidden cells after processing their markers. */
* osc133 marker processing continues while folded, but
* cell output does not.
*/
if (fold.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 (fold.tree) if (fold.tree)
window_copy_set_line(data, dy, y, 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 && 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 */ /* Continue the gutter tree on empty lines. */
window_copy_set_line(data, dy, y, fold.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) &&
(!fold.hiding || (fold.initial && y == 0))) { (!fold.hiding || (fold.initial && y == 0))) {
/* /* Keep the initial region's control line visible. */
* Hide folded line breaks, except after the initial
* synthetic command line (top line of copy buffer).
*/
screen_write_carriagereturn(&ctx); screen_write_carriagereturn(&ctx);
screen_write_linefeed(&ctx, 0, 8); screen_write_linefeed(&ctx, 0, 8);
} }
if (fold.initial && y == 0) { if (fold.initial && y == 0) {
/* /* Fold initial output below its control line. */
* 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.tree = 1; fold.tree = 1;
fold.hiding = data->source_flags[0] & fold.hiding = data->source_flags[0] &
WINDOW_COPY_FOLD_COLLAPSED; WINDOW_COPY_FOLD_COLLAPSED;
@@ -1029,11 +966,10 @@ window_copy_sync_backing(struct window_mode_entry *wme)
struct screen *src = &wp->base; struct screen *src = &wp->base;
struct screen *dst = data->source; struct screen *dst = data->source;
struct grid *sg = src->grid; struct grid *sg = src->grid;
struct grid *dg = dst->grid; struct grid *dg;
u_int sy = sg->sy; u_int sy, old_hsize, new_hsize;
u_int old_hsize = dg->hsize; u_int added, collected, kept;
u_int new_hsize = sg->hsize; u_int old_count, new_count;
u_int added, collected, kept, old_count, new_count;
/* /*
* Only a pane's own live grid is tracked incrementally. A different * 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) if (data->source == NULL || data->viewmode || wme->swp != wme->wp)
return (0); 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. */ /* Indices only line up at the same size and generation. */
if (sg->sx != dg->sx || sg->sy != dg->sy || 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; kept = old_hsize - collected;
old_count = old_hsize + sy; old_count = old_hsize + sy;
new_count = new_hsize + sy; new_count = new_hsize + sy;
if (collected != 0) { if (collected != 0)
memmove(data->source_flags, data->source_flags + collected, memmove(data->source_flags, data->source_flags + collected,
(old_count - collected) * sizeof *data->source_flags); (old_count - collected) * sizeof *data->source_flags);
}
data->source_flags = xreallocarray(data->source_flags, new_count, data->source_flags = xreallocarray(data->source_flags, new_count,
sizeof *data->source_flags); sizeof *data->source_flags);
if (new_count > old_count - collected) { if (new_count > old_count - collected)
memset(data->source_flags + old_count - collected, 0, memset(data->source_flags + old_count - collected, 0,
(new_count - (old_count - collected)) * (new_count - (old_count - collected)) *
sizeof *data->source_flags); sizeof *data->source_flags);
}
data->source_line_count = new_count; data->source_line_count = new_count;
if (added == 0 && collected == 0) { if (added == 0 && collected == 0) {
@@ -1194,7 +1132,8 @@ window_copy_init(struct window_mode_entry *wme,
window_copy_find_fold(wme, cy, &prompt, &chosen)) { window_copy_find_fold(wme, cy, &prompt, &chosen)) {
restored = window_copy_restore_cursor(wme, cx, cy); restored = window_copy_restore_cursor(wme, cx, cy);
if (!restored) 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) } else if (data->fold_view)
restored = window_copy_restore_cursor(wme, cx, cy); restored = window_copy_restore_cursor(wme, cx, cy);
if (!restored && cy < screen_hsize(data->backing)) { if (!restored && cy < screen_hsize(data->backing)) {
@@ -2187,6 +2126,7 @@ window_copy_cmd_select_output(struct window_copy_cmd_state *cs)
.action = WINDOW_COPY_FOLD_EXPAND .action = WINDOW_COPY_FOLD_EXPAND
}; };
struct grid_reader gr; struct grid_reader gr;
struct grid *gd;
void *buf; void *buf;
size_t len; size_t len;
u_int sx, sy, ex, ey, bsx, bsy, bex, bey; 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'); int all = args_has(cs->wargs, 'a');
if (all) { if (all) {
gd = data->backing->grid;
if (data->source != NULL)
gd = data->source->grid;
sx = sy = 0; sx = sy = 0;
total = data->source->grid->hsize + data->source->grid->sy; total = gd->hsize + gd->sy;
ey = total - 1; 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)) } else if (!window_copy_find_output_range(wme, &sx, &sy, &ex, &ey))
return (WINDOW_COPY_CMD_NOTHING); return (WINDOW_COPY_CMD_NOTHING);
buf = window_copy_get_output(wme, &len, all); 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++) { for (y = 0; y < gd->hsize + gd->sy; y++) {
gl = grid_get_line(gd, y); gl = grid_get_line(gd, y);
if (gl->flags & GRID_LINE_START_PROMPT) { if (gl->flags & GRID_LINE_START_PROMPT) {
if (have_prompt && target >= last_prompt && target < y && if (have_prompt && candidate != UINT_MAX &&
candidate != UINT_MAX) { target >= last_prompt && target < y) {
*prompt = last_prompt; *prompt = last_prompt;
*chosen = candidate; *chosen = candidate;
return (1); 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) { else if (line->source_line == UINT_MAX) {
*source_x = UINT_MAX; *source_x = UINT_MAX;
*source_y = data->source_line_count - 1; *source_y = data->source_line_count - 1;
} } else
else
*source_y = line->source_line; *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 *gd = data->backing->grid;
struct grid_line *gl; struct grid_line *gl;
struct window_copy_line *line; 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; u_int marker_x = 0, length, y;
int previous = 0; int previous = 0;
@@ -4354,9 +4297,10 @@ window_copy_set_fold(struct window_mode_entry *wme,
{ {
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;
u_int target = request->target; u_int target = request->target;
int changed = 0, enabled = 0, found, initial; int changed = 0, enabled = 0;
int found, initial;
int wanted; int wanted;
if (data->source == NULL) 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); window_copy_cursor_source(wme, &source_x, &source_y);
if (target == UINT_MAX) if (target == UINT_MAX)
target = source_y; target = source_y;
first_prompt = screen_hsize(data->source) + screen_size_y(data->source); first_prompt = window_copy_first_prompt(data->source->grid);
for (y = 0; y < first_prompt; y++) { initial = (first_prompt != 0 && first_prompt != UINT_MAX &&
if (grid_get_line(data->source->grid, y)->flags & target < first_prompt);
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;
if (!data->fold_view) { if (!data->fold_view) {
data->fold_view = 1; data->fold_view = 1;
window_copy_rebuild_backing(wme); window_copy_rebuild_backing(wme);
enabled = 1; enabled = 1;
} }
found = window_copy_find_fold(wme, target, &prompt, &chosen); found = window_copy_find_fold(wme, target, &prompt, &chosen);
if (request->all) {
wanted = (request->action == WINDOW_COPY_FOLD_COLLAPSE); wanted = (request->action == WINDOW_COPY_FOLD_COLLAPSE);
if (request->all) {
if (request->action == WINDOW_COPY_FOLD_TOGGLE) 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 = (request->action == WINDOW_COPY_FOLD_COLLAPSE);
if (request->action == WINDOW_COPY_FOLD_TOGGLE) 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)) { changed = window_copy_set_source_folded(data, 0, wanted);
changed = 1;
}
} else if (found && chosen != UINT_MAX) { } else if (found && chosen != UINT_MAX) {
wanted = (request->action == WINDOW_COPY_FOLD_COLLAPSE);
if (request->action == WINDOW_COPY_FOLD_TOGGLE) 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)) { changed = window_copy_set_source_folded(data, chosen, wanted);
changed = 1;
}
} }
if (!changed) { if (!changed) {
if (enabled) { if (enabled) {
@@ -4469,26 +4399,31 @@ window_copy_cmd_toggle_output(struct window_copy_cmd_state *cs)
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 current_found; int current_found;
int initial, target_found; int initial;
if (args_has(cs->wargs, 'm')) { if (args_has(cs->wargs, 'm')) {
if (cs->m == NULL || cmd_mouse_at(cs->wme->wp, cs->m, &x, &y, if (cs->m == NULL)
0) != 0 || x >= window_copy_left_margin(cs->wme)) 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); return (WINDOW_COPY_CMD_NOTHING);
target = screen_hsize(data->backing) + y - data->oy; target = screen_hsize(data->backing) + y - data->oy;
line = window_copy_get_line_info(data, target); line = window_copy_get_line_info(data, target);
if (line == NULL || line->source_line == UINT_MAX) if (line == NULL || line->source_line == UINT_MAX)
return (WINDOW_COPY_CMD_NOTHING); return (WINDOW_COPY_CMD_NOTHING);
initial = line->flags & WINDOW_COPY_LINE_INITIAL; initial = line->flags & WINDOW_COPY_LINE_INITIAL;
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); window_copy_cursor_source(cs->wme, &x, &current);
current_found = window_copy_find_fold(cs->wme, current, current_found = window_copy_find_fold(cs->wme, current,
&current_prompt, &chosen); &current_prompt, &chosen);
target_found = initial || window_copy_find_fold(cs->wme, request.command_end = !current_found ||
line->source_line, &target_prompt, &chosen);
if (!target_found)
return (WINDOW_COPY_CMD_NOTHING);
request.command_end = initial || !current_found ||
current_prompt != target_prompt; current_prompt != target_prompt;
}
request.target = line->source_line; request.target = line->source_line;
} }
if (window_copy_set_fold(cs->wme, &request)) 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_action action;
enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER; enum window_copy_cmd_clear clear = WINDOW_COPY_CMD_CLEAR_NEVER;
const char *command; 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; int keys, flags, mouse_control = 0;
char *error = NULL; char *error = NULL;
@@ -6606,12 +6542,12 @@ static u_int
window_copy_left_margin(struct window_mode_entry *wme) window_copy_left_margin(struct window_mode_entry *wme)
{ {
struct window_copy_mode_data *data = wme->data; 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) if (data->fold_view)
width = data->output_status_width + 2; width += data->output_status_width + 2;
return (width);
return (window_copy_line_number_width(wme) + width);
} }
static u_int static u_int
@@ -6702,6 +6638,61 @@ window_copy_get_current_offset(struct window_pane *wp, u_int *offset,
return (1); 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 static void
window_copy_write_line(struct window_mode_entry *wme, window_copy_write_line(struct window_mode_entry *wme,
struct screen_write_ctx *ctx, u_int py) 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 options *oo = wp->window->options;
struct grid_cell gc, mgc, cgc, mkgc, clgc, ln_gc; struct grid_cell gc, mgc, cgc, mkgc, clgc, ln_gc;
struct grid_cell cur_ln_gc; struct grid_cell cur_ln_gc;
struct grid_cell control_gc; const struct grid_cell *line_gc;
struct window_copy_line *line, *next;
u_int sx = screen_size_x(s); u_int sx = screen_size_x(s);
u_int hsize = screen_hsize(data->backing); 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 absolute, line_number, content_sx;
u_int i;
const char *value; const char *value;
char *expanded, *status; char *expanded;
struct format_tree *ft; struct format_tree *ft;
char control;
int current, mode; int current, mode;
line_width = window_copy_line_number_width(wme); line_width = window_copy_line_number_width(wme);
gutter_width = data->fold_view ? data->output_status_width + 2 : 0; width = window_copy_left_margin(wme);
width = line_width + gutter_width;
if (width >= sx) if (width >= sx)
content_sx = 1; content_sx = 1;
else if (width != 0) else if (width != 0)
@@ -6755,6 +6742,7 @@ window_copy_write_line(struct window_mode_entry *wme,
"copy-mode-current-line-number-style", ft); "copy-mode-current-line-number-style", ft);
cur_ln_gc.flags |= GRID_FLAG_NOPALETTE; cur_ln_gc.flags |= GRID_FLAG_NOPALETTE;
current = (py == data->cy); current = (py == data->cy);
line_gc = current ? &cur_ln_gc : &ln_gc;
backing_y = hsize - data->oy + py; backing_y = hsize - data->oy + py;
absolute = backing_y + 1; absolute = backing_y + 1;
if (data->lines != NULL && backing_y < data->line_count && 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; line_number = data->cy - py;
if (line_width != 0) { if (line_width != 0) {
screen_write_cursormove(ctx, 0, py, 0); screen_write_cursormove(ctx, 0, py, 0);
screen_write_nputs(ctx, line_width, screen_write_nputs(ctx, line_width, line_gc, "%*u ",
current ? &cur_ln_gc : &ln_gc, "%*u ",
(int)line_width - 1, line_number); (int)line_width - 1, line_number);
} }
if (gutter_width != 0) { if (data->fold_view)
line = NULL; window_copy_write_gutter(wme, ctx, py, line_width,
control = ' '; line_gc);
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);
}
}
} }
window_copy_write_one(wme, ctx, width, py, hsize - data->oy + py, 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); expanded = format_expand(ft, value);
if (*expanded != '\0') { if (*expanded != '\0') {
screen_write_cursormove(ctx, width, 0, 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); free(expanded);
} }
@@ -7456,7 +7395,8 @@ window_copy_find_previous_output_range(struct screen *s, u_int cursor_x,
struct osc133_data *od; struct osc133_data *od;
u_int start_x, start_y, end_x, end_y; u_int start_x, start_y, end_x, end_y;
u_int y, total; 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 pending = 0, started_here;
int has_start, has_end, end_first; 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_x = od->out_end_col;
end_y = y; end_y = y;
/* /* Prefer earlier output to a later empty command. */
* 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).
*/
if (!found || found_empty || if (!found || found_empty ||
start_x != end_x || start_y != end_y) { start_x != end_x || start_y != end_y) {
*sx = start_x; *sx = start_x;
@@ -7538,12 +7473,11 @@ window_copy_get_output(struct window_mode_entry *wme, size_t *len, int all)
{ {
struct window_copy_mode_data *data = wme->data; struct window_copy_mode_data *data = wme->data;
struct grid *gd; struct grid *gd;
void *buf;
u_int sx, sy, ex, ey, total; u_int sx, sy, ex, ey, total;
if (data->source == NULL) { gd = data->backing->grid;
*len = 0; if (data->source != NULL)
return (NULL);
}
gd = data->source->grid; gd = data->source->grid;
total = gd->hsize + gd->sy; total = gd->hsize + gd->sy;
if (all) { if (all) {
@@ -7554,7 +7488,8 @@ window_copy_get_output(struct window_mode_entry *wme, size_t *len, int all)
*len = 0; *len = 0;
return (NULL); 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 * static void *