mirror of
https://github.com/tmux/tmux.git
synced 2026-10-09 11:44:23 +00:00
Add commands to copy mode to select, pipe and open OSC 133 command
output, from Michael Grant.
This commit is contained in:
207
grid-reader.c
207
grid-reader.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: grid-reader.c,v 1.11 2026/07/29 17:42:56 nicm Exp $ */
|
||||
/* $OpenBSD: grid-reader.c,v 1.12 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Anindya Mukherjee <anindya49@hotmail.com>
|
||||
@@ -436,3 +436,208 @@ grid_reader_cursor_back_to_indentation(struct grid_reader *gr)
|
||||
gr->cx = oldx;
|
||||
gr->cy = oldy;
|
||||
}
|
||||
|
||||
/* Get the end of the last used line. */
|
||||
static void
|
||||
grid_reader_output_end(struct grid *gd, u_int *x, u_int *y)
|
||||
{
|
||||
u_int last = gd->hsize + gd->sy - 1;
|
||||
|
||||
while (last > 0 && grid_get_line(gd, last)->cellused == 0)
|
||||
last--;
|
||||
*x = grid_get_line(gd, last)->cellused;
|
||||
*y = last;
|
||||
}
|
||||
|
||||
/* Find the most recent complete output at or before the cursor. */
|
||||
static int
|
||||
grid_reader_previous_output_range(struct grid_reader *gr, u_int *sx,
|
||||
u_int *sy, u_int *ex, u_int *ey)
|
||||
{
|
||||
struct grid *gd = gr->gd;
|
||||
struct grid_line *gl;
|
||||
struct osc133_data *od;
|
||||
u_int cursor_x = gr->cx, cursor_y = gr->cy;
|
||||
u_int start_x, start_y, y, total;
|
||||
int found = 0, have_prompt = 0;
|
||||
int pending = 0;
|
||||
int has_start, has_end, end_first;
|
||||
int cleared;
|
||||
|
||||
total = gd->hsize + gd->sy;
|
||||
|
||||
/*
|
||||
* Scan from the top because markers arrive in their natural order, C
|
||||
* then D. A backward scan would meet D first and need extra state to
|
||||
* find its C. The last complete output found is the one wanted.
|
||||
*/
|
||||
for (y = 0; y < total && y <= cursor_y; y++) {
|
||||
gl = grid_get_line(gd, y);
|
||||
od = &gl->osc133_data;
|
||||
|
||||
/* On the cursor's line, ignore markers after the cursor. */
|
||||
has_start = has_end = end_first = 0;
|
||||
if (gl->flags & GRID_LINE_START_OUTPUT) {
|
||||
if (y != cursor_y || od->out_start_col <= cursor_x)
|
||||
has_start = 1;
|
||||
}
|
||||
if (gl->flags & GRID_LINE_END_OUTPUT) {
|
||||
if (y != cursor_y || od->out_end_col <= cursor_x)
|
||||
has_end = 1;
|
||||
}
|
||||
|
||||
/* An end before the start ends the previous output. */
|
||||
if (has_start && has_end && od->out_end_col < od->out_start_col)
|
||||
end_first = 1;
|
||||
|
||||
/* A C marker starts an output that is pending until its D. */
|
||||
if (has_start && !end_first) {
|
||||
start_x = od->out_start_col;
|
||||
start_y = y;
|
||||
pending = 1;
|
||||
}
|
||||
|
||||
/* The output may have cleared its C marker from the screen. */
|
||||
if (!pending && !have_prompt &&
|
||||
(gl->flags & GRID_LINE_END_OUTPUT)) {
|
||||
cleared = 1;
|
||||
if ((gl->flags & GRID_LINE_START_PROMPT) &&
|
||||
od->out_end_col > od->prompt_col)
|
||||
cleared = 0;
|
||||
if (cleared) {
|
||||
start_x = start_y = 0;
|
||||
pending = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* A D marker completes the pending output. */
|
||||
if (pending && has_end) {
|
||||
*sx = start_x;
|
||||
*sy = start_y;
|
||||
*ex = od->out_end_col;
|
||||
*ey = y;
|
||||
found = 1;
|
||||
pending = 0;
|
||||
}
|
||||
if (has_start && end_first) {
|
||||
start_x = od->out_start_col;
|
||||
start_y = y;
|
||||
pending = 1;
|
||||
}
|
||||
|
||||
/* A prompt drops an unfinished output. */
|
||||
if (gl->flags & GRID_LINE_START_PROMPT) {
|
||||
if (!has_start || od->out_start_col < od->prompt_col)
|
||||
pending = 0;
|
||||
have_prompt = 1;
|
||||
}
|
||||
}
|
||||
return (found);
|
||||
}
|
||||
|
||||
/*
|
||||
* Find the output range for the command at the cursor, from its C marker to
|
||||
* its D marker. If the prompt has no output yet, use the previous output.
|
||||
* Returns 0 if there is no usable range.
|
||||
*/
|
||||
int
|
||||
grid_reader_output_range(struct grid_reader *gr, u_int *sx, u_int *sy,
|
||||
u_int *ex, u_int *ey)
|
||||
{
|
||||
struct grid *gd = gr->gd;
|
||||
struct grid_line *gl;
|
||||
struct osc133_data *od;
|
||||
u_int cursor_x = gr->cx, cursor_y = gr->cy;
|
||||
u_int prompt_x = 0, prompt_y = UINT_MAX;
|
||||
u_int y, total;
|
||||
int found_start = 0, found_end = 0;
|
||||
int next_prompt, in_range, end_ok;
|
||||
int found;
|
||||
|
||||
/* Find the last prompt at or before the cursor. */
|
||||
total = gd->hsize + gd->sy;
|
||||
for (y = 0; y < total; y++) {
|
||||
gl = grid_get_line(gd, y);
|
||||
od = &gl->osc133_data;
|
||||
if (~gl->flags & GRID_LINE_START_PROMPT)
|
||||
continue;
|
||||
if (y > cursor_y)
|
||||
break;
|
||||
if (y == cursor_y && od->prompt_col > cursor_x)
|
||||
break;
|
||||
prompt_y = y;
|
||||
prompt_x = od->prompt_col;
|
||||
}
|
||||
|
||||
/* With no prompt, its A marker may have left history, so start at 0. */
|
||||
if (prompt_y == UINT_MAX)
|
||||
y = 0;
|
||||
else
|
||||
y = prompt_y;
|
||||
|
||||
/* Walk down from the prompt to its C and D, up to the next prompt. */
|
||||
for (; y < total; y++) {
|
||||
gl = grid_get_line(gd, y);
|
||||
od = &gl->osc133_data;
|
||||
next_prompt = 0;
|
||||
if (y != prompt_y && (gl->flags & GRID_LINE_START_PROMPT))
|
||||
next_prompt = 1;
|
||||
|
||||
/* Output before the next prompt on its line is ours. */
|
||||
if (y == prompt_y)
|
||||
in_range = (od->out_start_col >= prompt_x);
|
||||
else if (next_prompt)
|
||||
in_range = (od->out_start_col < od->prompt_col);
|
||||
else
|
||||
in_range = 1;
|
||||
if (gl->flags & GRID_LINE_START_OUTPUT && in_range) {
|
||||
*sx = od->out_start_col;
|
||||
*sy = y;
|
||||
found_start = 1;
|
||||
}
|
||||
|
||||
/* Both A and C may have left history while D remains. */
|
||||
if (!found_start && prompt_y == UINT_MAX &&
|
||||
(gl->flags & GRID_LINE_END_OUTPUT)) {
|
||||
if (!next_prompt || od->out_end_col <= od->prompt_col) {
|
||||
*sx = *sy = 0;
|
||||
found_start = 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* An output may end on the same line or the next prompt's. */
|
||||
if (found_start && (gl->flags & GRID_LINE_END_OUTPUT)) {
|
||||
end_ok = 1;
|
||||
if (y == prompt_y && od->out_end_col < prompt_x)
|
||||
end_ok = 0;
|
||||
if (y == *sy && od->out_end_col < *sx)
|
||||
end_ok = 0;
|
||||
if (end_ok) {
|
||||
*ex = od->out_end_col;
|
||||
*ey = y;
|
||||
found_end = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (next_prompt)
|
||||
break;
|
||||
}
|
||||
|
||||
/* The cursor is on a prompt with no output yet. */
|
||||
if (!found_start) {
|
||||
found = grid_reader_previous_output_range(gr, sx, sy, ex, ey);
|
||||
return (found);
|
||||
}
|
||||
|
||||
/*
|
||||
* Without a D marker the command is still running, so its output runs
|
||||
* to the last used line. If the next prompt came first, it has no
|
||||
* usable end.
|
||||
*/
|
||||
if (!found_end) {
|
||||
if (y != total)
|
||||
return (0);
|
||||
grid_reader_output_end(gd, ex, ey);
|
||||
}
|
||||
return (1);
|
||||
}
|
||||
|
||||
81
grid.c
81
grid.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: grid.c,v 1.158 2026/09/01 12:49:49 nicm Exp $ */
|
||||
/* $OpenBSD: grid.c,v 1.159 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2008 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -1296,12 +1296,60 @@ grid_reflow_move(struct grid *gd, struct grid_line *from)
|
||||
return (to);
|
||||
}
|
||||
|
||||
/* Move the OSC 133 markers from one line to another. */
|
||||
static void
|
||||
grid_reflow_move_osc133(struct grid_line *to, struct grid_line *from,
|
||||
u_int to_col, u_int count, int last)
|
||||
{
|
||||
struct osc133_data *src = &from->osc133_data;
|
||||
struct osc133_data *dst = &to->osc133_data;
|
||||
u_short *src_col[] = {
|
||||
&src->prompt_col,
|
||||
&src->cmd_col,
|
||||
&src->out_start_col,
|
||||
&src->out_end_col
|
||||
};
|
||||
u_short *dst_col[] = {
|
||||
&dst->prompt_col,
|
||||
&dst->cmd_col,
|
||||
&dst->out_start_col,
|
||||
&dst->out_end_col
|
||||
};
|
||||
u_int flags[] = {
|
||||
GRID_LINE_START_PROMPT|GRID_LINE_SECOND_PROMPT,
|
||||
GRID_LINE_START_COMMAND,
|
||||
GRID_LINE_START_OUTPUT,
|
||||
GRID_LINE_END_OUTPUT
|
||||
};
|
||||
u_int i, flag;
|
||||
|
||||
if ((from->flags & GRID_LINE_OSC133_FLAGS) == 0)
|
||||
return;
|
||||
for (i = 0; i < nitems(flags); i++) {
|
||||
flag = from->flags & flags[i];
|
||||
if (flag == 0)
|
||||
continue;
|
||||
if (!last && *src_col[i] >= count) {
|
||||
*src_col[i] -= count;
|
||||
continue;
|
||||
}
|
||||
*dst_col[i] = to_col + *src_col[i];
|
||||
to->flags |= flag;
|
||||
from->flags &= ~flag;
|
||||
*src_col[i] = 0;
|
||||
if (flag == GRID_LINE_END_OUTPUT) {
|
||||
dst->exit_status = src->exit_status;
|
||||
src->exit_status = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Join line below onto this one. */
|
||||
static void
|
||||
grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
u_int width, int already)
|
||||
{
|
||||
struct grid_line *gl, *from = NULL;
|
||||
struct grid_line *gl, *from = NULL, *next;
|
||||
struct grid_cell gc;
|
||||
u_int lines, left, i, to, line, want = 0;
|
||||
u_int at;
|
||||
@@ -1332,13 +1380,20 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
break;
|
||||
line = yy + 1 + lines;
|
||||
|
||||
/* If the next line is empty, skip it. */
|
||||
/* Consume empty lines, including any markers. */
|
||||
if (~gd->linedata[line].flags & GRID_LINE_WRAPPED)
|
||||
wrapped = 0;
|
||||
if (gd->linedata[line].cellused == 0) {
|
||||
next = &gd->linedata[line];
|
||||
if (!wrapped &&
|
||||
(next->flags & GRID_LINE_OSC133_FLAGS) == 0)
|
||||
break;
|
||||
from = next;
|
||||
want = 0;
|
||||
grid_reflow_move_osc133(gl, from, at, 0, 1);
|
||||
lines++;
|
||||
if (!wrapped)
|
||||
break;
|
||||
lines++;
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1365,6 +1420,8 @@ grid_reflow_join(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
grid_set_cell(target, at, to, &gc);
|
||||
at++;
|
||||
}
|
||||
grid_reflow_move_osc133(gl, from, at - want, want,
|
||||
want == from->cellused);
|
||||
lines++;
|
||||
|
||||
/*
|
||||
@@ -1435,6 +1492,14 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
line = target->sy + 1;
|
||||
first = grid_reflow_add(target, lines);
|
||||
|
||||
/* The first line keeps only the markers within its first at cells. */
|
||||
memcpy(first, gl, sizeof *first);
|
||||
first->cellsize = first->cellused = at;
|
||||
first->flags &= ~GRID_LINE_OSC133_FLAGS;
|
||||
first->flags |= GRID_LINE_WRAPPED;
|
||||
memset(&first->osc133_data, 0, sizeof first->osc133_data);
|
||||
grid_reflow_move_osc133(first, gl, 0, at, 0);
|
||||
|
||||
/* Copy sections from the original line. */
|
||||
width = 0;
|
||||
xx = 0;
|
||||
@@ -1442,6 +1507,8 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
grid_get_cell1(gl, i, &gc);
|
||||
if (width + gc.data.width > sx) {
|
||||
target->linedata[line].flags |= GRID_LINE_WRAPPED;
|
||||
grid_reflow_move_osc133(&target->linedata[line], gl, 0,
|
||||
xx, 0);
|
||||
|
||||
line++;
|
||||
width = 0;
|
||||
@@ -1453,11 +1520,7 @@ grid_reflow_split(struct grid *target, struct grid *gd, u_int sx, u_int yy,
|
||||
}
|
||||
if (flags & GRID_LINE_WRAPPED)
|
||||
target->linedata[line].flags |= GRID_LINE_WRAPPED;
|
||||
|
||||
/* Move the remainder of the original line. */
|
||||
gl->cellsize = gl->cellused = at;
|
||||
gl->flags |= GRID_LINE_WRAPPED;
|
||||
memcpy(first, gl, sizeof *first);
|
||||
grid_reflow_move_osc133(&target->linedata[line], gl, 0, xx, 1);
|
||||
grid_reflow_dead(gl);
|
||||
|
||||
/* Adjust the scroll position. */
|
||||
|
||||
3
input.c
3
input.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: input.c,v 1.272 2026/09/21 10:22:31 nicm Exp $ */
|
||||
/* $OpenBSD: input.c,v 1.273 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -3241,7 +3241,6 @@ input_osc_133(struct input_ctx *ictx, const char *p)
|
||||
case 'A':
|
||||
case 'N':
|
||||
if (gl != NULL) {
|
||||
memset(&gl->osc133_data, 0, sizeof gl->osc133_data);
|
||||
gl->osc133_data.prompt_col = s->cx;
|
||||
gl->flags |= GRID_LINE_START_PROMPT;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: key-bindings.c,v 1.193 2026/09/21 20:45:48 nicm Exp $ */
|
||||
/* $OpenBSD: key-bindings.c,v 1.194 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -53,6 +53,7 @@
|
||||
" '#{?#{m/r:(copy|view)-mode,#{pane_mode}},Go To Bottom,}' '>' {send -X history-bottom}" \
|
||||
" ''" \
|
||||
" '#{?#{==:#{pane_mode},copy-mode},#{?copy_line_numbers,Hide Line Numbers,Show Line Numbers},}' 'L' {send -X line-numbers-toggle}" \
|
||||
" '#{?#{==:#{pane_mode},copy-mode},Edit,}' 'e' {if -F '#{selection_present}' {send -X open-selection} {send -X open-output}}" \
|
||||
" '#{?#{==:#{pane_mode},copy-mode},#{?refresh_active,Refresh Off,Refresh On},}' 'r' {send -X refresh-toggle}" \
|
||||
" ''" \
|
||||
" '#{?#{&&:#{buffer_size},#{!:#{pane_in_mode}}},Paste #[underscore]#{=/9/...:buffer_sample},}' 'p' {paste-buffer}" \
|
||||
@@ -575,6 +576,7 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode C-l { send -X recentre-top-bottom }",
|
||||
"bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }",
|
||||
"bind -Tcopy-mode C-n { send -X cursor-down }",
|
||||
"bind -Tcopy-mode C-o { send -X select-output }",
|
||||
"bind -Tcopy-mode C-p { send -X cursor-up }",
|
||||
"bind -Tcopy-mode C-r { command-prompt -P -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental -- '%%' } }",
|
||||
"bind -Tcopy-mode C-s { command-prompt -P -T search -ip'(search down)' -I'#{pane_search_string}' { send -X search-forward-incremental -- '%%' } }",
|
||||
@@ -592,6 +594,7 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode R { send -X rectangle-toggle }",
|
||||
"bind -Tcopy-mode T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
|
||||
"bind -Tcopy-mode X { send -X set-mark }",
|
||||
"bind -Tcopy-mode e { if -F '#{selection_present}' { send -X open-selection } { send -X open-output } }",
|
||||
"bind -Tcopy-mode f { command-prompt -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
|
||||
"bind -Tcopy-mode g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
|
||||
"bind -Tcopy-mode n { send -X search-again }",
|
||||
@@ -687,6 +690,8 @@ key_bindings_init(void)
|
||||
"bind -Tcopy-mode-vi K { send -X scroll-up }",
|
||||
"bind -Tcopy-mode-vi L { send -X bottom-line }",
|
||||
"bind -Tcopy-mode-vi M { send -X middle-line }",
|
||||
"bind -Tcopy-mode-vi M-e { if -F '#{selection_present}' { send -X open-selection } { send -X open-output } }",
|
||||
"bind -Tcopy-mode-vi M-o { send -X select-output }",
|
||||
"bind -Tcopy-mode-vi N { send -X search-reverse }",
|
||||
"bind -Tcopy-mode-vi P { send -X toggle-position }",
|
||||
"bind -Tcopy-mode-vi T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
|
||||
|
||||
8
spawn.c
8
spawn.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: spawn.c,v 1.56 2026/10/08 07:06:26 nicm Exp $ */
|
||||
/* $OpenBSD: spawn.c,v 1.57 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2019 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -685,7 +685,7 @@ spawn_editor_finish(struct window_pane *wp)
|
||||
|
||||
struct spawn_editor_state *
|
||||
spawn_editor(struct client *c, const char *buf, size_t len,
|
||||
spawn_finish_edit_cb cb, void *arg)
|
||||
const char *editor, spawn_finish_edit_cb cb, void *arg)
|
||||
{
|
||||
struct spawn_editor_state *es;
|
||||
struct spawn_context sc = { 0 };
|
||||
@@ -699,13 +699,13 @@ spawn_editor(struct client *c, const char *buf, size_t len,
|
||||
FILE *f;
|
||||
char *cmd, *cause = NULL;
|
||||
char path[] = _PATH_TMP "tmux.XXXXXXXX";
|
||||
const char *editor;
|
||||
int fd;
|
||||
|
||||
if (w->modal != NULL)
|
||||
return (NULL);
|
||||
|
||||
editor = options_get_string(global_options, "editor");
|
||||
if (editor == NULL)
|
||||
editor = options_get_string(global_options, "editor");
|
||||
fd = mkstemp(path);
|
||||
if (fd == -1)
|
||||
return (NULL);
|
||||
|
||||
70
tmux.1
70
tmux.1
@@ -1,4 +1,4 @@
|
||||
.\" $OpenBSD: tmux.1,v 1.1180 2026/10/07 13:13:49 nicm Exp $
|
||||
.\" $OpenBSD: tmux.1,v 1.1181 2026/10/08 07:50:05 nicm Exp $
|
||||
.\"
|
||||
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
.\"
|
||||
@@ -14,7 +14,7 @@
|
||||
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
||||
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
.\"
|
||||
.Dd $Mdocdate: October 7 2026 $
|
||||
.Dd $Mdocdate: October 8 2026 $
|
||||
.Dt TMUX 1
|
||||
.Os
|
||||
.Sh NAME
|
||||
@@ -2011,6 +2011,25 @@ Same as
|
||||
.Ic copy\-pipe
|
||||
but also exit copy mode.
|
||||
.It Xo
|
||||
.Ic copy\-pipe\-output
|
||||
.Op Fl aCP
|
||||
.Op Ar command
|
||||
.Op Ar prefix
|
||||
.Xc
|
||||
Copy the current OSC 133 command output, pipe it to
|
||||
.Ar command ,
|
||||
and clear the selection.
|
||||
.Ar prefix
|
||||
is used to name the new paste buffer.
|
||||
.It Xo
|
||||
.Ic copy\-output
|
||||
.Op Fl aCP
|
||||
.Op Ar prefix
|
||||
.Xc
|
||||
Copy the current OSC 133 command output and clear the selection.
|
||||
.Ar prefix
|
||||
is used to name the new paste buffer.
|
||||
.It Xo
|
||||
.Ic copy\-selection
|
||||
.Op Fl CP
|
||||
.Op Ar prefix
|
||||
@@ -2229,6 +2248,31 @@ but treat all non-whitespace characters as part of a word.
|
||||
.Xc
|
||||
Switch at which end of the selection the cursor sits.
|
||||
.It Xo
|
||||
.Ic open\-output
|
||||
.Op Fl a
|
||||
.Op Ar command
|
||||
(vi: M\-e)
|
||||
(emacs: e)
|
||||
.Xc
|
||||
Open the current OSC 133 command output in an editor.
|
||||
If
|
||||
.Ar command
|
||||
is given, it is used as the editor command; otherwise, the
|
||||
.Ic editor
|
||||
option is used.
|
||||
.It Xo
|
||||
.Ic open\-selection
|
||||
.Op Ar command
|
||||
(vi: M\-e)
|
||||
(emacs: e)
|
||||
.Xc
|
||||
Open the current selection in an editor.
|
||||
If
|
||||
.Ar command
|
||||
is given, it is used as the editor command; otherwise, the
|
||||
.Ic editor
|
||||
option is used.
|
||||
.It Xo
|
||||
.Ic page\-down
|
||||
(vi: C\-f)
|
||||
(emacs: PageDown)
|
||||
@@ -2269,6 +2313,21 @@ Same as
|
||||
.Ic pipe
|
||||
but also exit copy mode.
|
||||
.It Xo
|
||||
.Ic pipe\-output
|
||||
.Op Fl a
|
||||
.Op Ar command
|
||||
.Xc
|
||||
Pipe the current OSC 133 command output to
|
||||
.Ar command
|
||||
and clear the selection.
|
||||
.It Xo
|
||||
.Ic pipe\-selection
|
||||
.Op Ar command
|
||||
.Xc
|
||||
Pipe the selected text to
|
||||
.Ar command
|
||||
and clear the selection.
|
||||
.It Xo
|
||||
.Ic previous\-matching\-bracket
|
||||
(emacs: M\-C\-b)
|
||||
.Xc
|
||||
@@ -2458,6 +2517,13 @@ Search forward for the specified plain text.
|
||||
Repeat the last search in the reverse direction (forward becomes backward and
|
||||
backward becomes forward).
|
||||
.It Xo
|
||||
.Ic select\-output
|
||||
.Op Fl a
|
||||
(vi: M\-o)
|
||||
(emacs: C\-o)
|
||||
.Xc
|
||||
Select the current OSC 133 command output.
|
||||
.It Xo
|
||||
.Ic select\-line
|
||||
(vi: V)
|
||||
.Xc
|
||||
|
||||
6
tmux.h
6
tmux.h
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: tmux.h,v 1.1453 2026/10/06 17:49:45 nicm Exp $ */
|
||||
/* $OpenBSD: tmux.h,v 1.1454 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -3487,6 +3487,8 @@ void grid_reader_start(struct grid_reader *, struct grid *, u_int, u_int);
|
||||
void grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *);
|
||||
u_int grid_reader_line_length(struct grid_reader *);
|
||||
int grid_reader_in_set(struct grid_reader *, const char *);
|
||||
int grid_reader_output_range(struct grid_reader *, u_int *, u_int *,
|
||||
u_int *, u_int *);
|
||||
void grid_reader_cursor_right(struct grid_reader *, int, int, int);
|
||||
void grid_reader_cursor_left(struct grid_reader *, int);
|
||||
void grid_reader_cursor_down(struct grid_reader *);
|
||||
@@ -4171,7 +4173,7 @@ struct winlink *spawn_window(struct spawn_context *, char **);
|
||||
struct window_pane *spawn_pane(struct spawn_context *, char **);
|
||||
typedef void (*spawn_finish_edit_cb)(char *, size_t, void *);
|
||||
struct spawn_editor_state *spawn_editor(struct client *, const char *, size_t,
|
||||
spawn_finish_edit_cb, void *);
|
||||
const char *, spawn_finish_edit_cb, void *);
|
||||
void spawn_cancel_editor(struct spawn_editor_state *);
|
||||
pid_t spawn_get_editor_pid(struct spawn_editor_state *);
|
||||
void spawn_editor_finish(struct window_pane *);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-buffer.c,v 1.52 2026/08/24 21:19:40 nicm Exp $ */
|
||||
/* $OpenBSD: window-buffer.c,v 1.53 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2017 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -606,7 +606,8 @@ window_buffer_start_edit(struct window_buffer_modedata *data,
|
||||
ed->name = xstrdup(paste_buffer_name(pb));
|
||||
ed->pb = pb;
|
||||
|
||||
ed->editor = spawn_editor(c, buf, len, window_buffer_edit_close_cb, ed);
|
||||
ed->editor = spawn_editor(c, buf, len, NULL,
|
||||
window_buffer_edit_close_cb, ed);
|
||||
if (ed->editor == NULL)
|
||||
window_buffer_finish_edit(ed);
|
||||
else {
|
||||
|
||||
399
window-copy.c
399
window-copy.c
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-copy.c,v 1.436 2026/10/07 12:59:50 nicm Exp $ */
|
||||
/* $OpenBSD: window-copy.c,v 1.437 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -121,9 +121,17 @@ static int window_copy_set_selection(struct window_mode_entry *, int, int);
|
||||
static int window_copy_update_selection(struct window_mode_entry *, int,
|
||||
int);
|
||||
static void window_copy_synchronize_cursor(struct window_mode_entry *, int);
|
||||
static int window_copy_get_output_range(struct window_mode_entry *, int,
|
||||
u_int *, u_int *, u_int *, u_int *);
|
||||
static void *window_copy_get_selection(struct window_mode_entry *, size_t *);
|
||||
static void *window_copy_get_output(struct window_mode_entry *, size_t *,
|
||||
int);
|
||||
static void *window_copy_get_grid_range(struct window_mode_entry *, u_int,
|
||||
u_int, u_int, u_int, size_t *);
|
||||
static void window_copy_copy_buffer(struct window_mode_entry *,
|
||||
const char *, void *, size_t, int, int);
|
||||
static void window_copy_pipe_buffer(struct session *, const char *,
|
||||
void *, size_t);
|
||||
static void window_copy_pipe(struct window_mode_entry *,
|
||||
struct session *, const char *);
|
||||
static void window_copy_copy_pipe(struct window_mode_entry *,
|
||||
@@ -133,6 +141,8 @@ static void window_copy_copy_selection(struct window_mode_entry *,
|
||||
const char *, int, int);
|
||||
static void window_copy_append_selection(struct window_mode_entry *);
|
||||
static void window_copy_clear_selection(struct window_mode_entry *);
|
||||
static u_int window_copy_copy_line_length(struct window_mode_entry *,
|
||||
u_int, int *);
|
||||
static void window_copy_copy_line(struct window_mode_entry *, char **,
|
||||
size_t *, u_int, u_int, u_int);
|
||||
static int window_copy_in_set(struct window_mode_entry *, u_int, u_int,
|
||||
@@ -1155,7 +1165,9 @@ window_copy_formats(struct window_mode_entry *wme, struct format_tree *ft)
|
||||
format_add(ft, "selection_active", "1");
|
||||
else
|
||||
format_add(ft, "selection_active", "0");
|
||||
if (data->endselx != data->selx || data->endsely != data->sely)
|
||||
if (data->modekeys == MODEKEY_VI ||
|
||||
data->endselx != data->selx ||
|
||||
data->endsely != data->sely)
|
||||
format_add(ft, "selection_present", "1");
|
||||
else
|
||||
format_add(ft, "selection_present", "0");
|
||||
@@ -1605,6 +1617,84 @@ window_copy_cmd_copy_selection_and_cancel(struct window_copy_cmd_state *cs)
|
||||
return (WINDOW_COPY_CMD_CANCEL);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
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 grid_reader gr;
|
||||
void *buf;
|
||||
size_t len;
|
||||
u_int sx, sy, ex, ey, last;
|
||||
int all = args_has(cs->wargs, 'a');
|
||||
int wrapped;
|
||||
|
||||
if (!window_copy_get_output_range(wme, all, &sx, &sy, &ex, &ey))
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
|
||||
/* An empty output selects nothing. */
|
||||
buf = window_copy_get_grid_range(wme, sx, sy, ex, ey, &len);
|
||||
if (buf == NULL)
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
free(buf);
|
||||
|
||||
window_copy_clear_selection(wme);
|
||||
data->lineflag = LINE_SEL_NONE;
|
||||
data->rectflag = 0;
|
||||
data->selflag = SEL_CHAR;
|
||||
window_copy_scroll_to(wme, sx, sy, 1);
|
||||
window_copy_start_selection(wme);
|
||||
|
||||
/* The end is inclusive in vi mode, so step back onto the last cell. */
|
||||
if (data->modekeys == MODEKEY_VI) {
|
||||
last = window_copy_copy_line_length(wme, ey, NULL);
|
||||
if (ex > last)
|
||||
ex = last;
|
||||
if (ex == 0 && ey > 0) {
|
||||
ey--;
|
||||
ex = window_copy_copy_line_length(wme, ey, &wrapped);
|
||||
if (wrapped && ex > 0)
|
||||
ex--;
|
||||
} else {
|
||||
grid_reader_start(&gr, data->backing->grid, ex, ey);
|
||||
grid_reader_cursor_left(&gr, 1);
|
||||
grid_reader_get_cursor(&gr, &ex, &ey);
|
||||
}
|
||||
}
|
||||
window_copy_scroll_to(wme, ex, ey, 1);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_copy_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct client *c = cs->c;
|
||||
struct session *s = cs->s;
|
||||
struct winlink *wl = cs->wl;
|
||||
struct window_pane *wp = wme->wp;
|
||||
void *buf;
|
||||
char *prefix = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
size_t len;
|
||||
int all = args_has(cs->wargs, 'a');
|
||||
int set_paste = !args_has(cs->wargs, 'P');
|
||||
int set_clip = !args_has(cs->wargs, 'C');
|
||||
|
||||
if (arg0 != NULL)
|
||||
prefix = format_single(NULL, arg0, c, s, wl, wp);
|
||||
if (s != NULL) {
|
||||
buf = window_copy_get_output(wme, &len, all);
|
||||
if (buf != NULL) {
|
||||
window_copy_copy_buffer(wme, prefix, buf, len,
|
||||
set_paste, set_clip);
|
||||
}
|
||||
}
|
||||
free(prefix);
|
||||
window_copy_clear_selection(wme);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_cursor_down(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
@@ -2772,6 +2862,127 @@ window_copy_cmd_pipe_and_cancel(struct window_copy_cmd_state *cs)
|
||||
return (WINDOW_COPY_CMD_CANCEL);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_copy_pipe_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct client *c = cs->c;
|
||||
struct session *s = cs->s;
|
||||
struct winlink *wl = cs->wl;
|
||||
struct window_pane *wp = wme->wp;
|
||||
void *buf;
|
||||
char *command = NULL, *prefix = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
const char *arg1 = args_string(cs->wargs, 1);
|
||||
size_t len;
|
||||
int all = args_has(cs->wargs, 'a');
|
||||
int set_paste = !args_has(cs->wargs, 'P');
|
||||
int set_clip = !args_has(cs->wargs, 'C');
|
||||
|
||||
if (arg1 != NULL)
|
||||
prefix = format_single(NULL, arg1, c, s, wl, wp);
|
||||
if (s != NULL && arg0 != NULL && *arg0 != '\0')
|
||||
command = format_single(NULL, arg0, c, s, wl, wp);
|
||||
if (s != NULL) {
|
||||
buf = window_copy_get_output(wme, &len, all);
|
||||
if (buf != NULL) {
|
||||
window_copy_pipe_buffer(s, command, buf, len);
|
||||
window_copy_copy_buffer(wme, prefix, buf, len,
|
||||
set_paste, set_clip);
|
||||
}
|
||||
}
|
||||
free(command);
|
||||
free(prefix);
|
||||
window_copy_clear_selection(wme);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_pipe_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct client *c = cs->c;
|
||||
struct session *s = cs->s;
|
||||
struct winlink *wl = cs->wl;
|
||||
struct window_pane *wp = wme->wp;
|
||||
void *buf;
|
||||
char *command = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
size_t len;
|
||||
int all = args_has(cs->wargs, 'a');
|
||||
|
||||
if (s != NULL && arg0 != NULL && *arg0 != '\0')
|
||||
command = format_single(NULL, arg0, c, s, wl, wp);
|
||||
if (s != NULL) {
|
||||
buf = window_copy_get_output(wme, &len, all);
|
||||
if (buf != NULL) {
|
||||
window_copy_pipe_buffer(s, command, buf, len);
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
free(command);
|
||||
window_copy_clear_selection(wme);
|
||||
return (WINDOW_COPY_CMD_REDRAW);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_open_done(char *buf, __unused size_t len, __unused void *arg)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_open_buffer(struct window_copy_cmd_state *cs, void *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct window_pane *wp = wme->wp;
|
||||
char *editor = NULL;
|
||||
const char *arg0 = args_string(cs->wargs, 0);
|
||||
|
||||
if (arg0 != NULL && *arg0 != '\0')
|
||||
editor = format_single(NULL, arg0, cs->c, cs->s, cs->wl, wp);
|
||||
spawn_editor(cs->c, buf, len, editor, window_copy_open_done, NULL);
|
||||
free(editor);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_open_selection(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
void *buf;
|
||||
size_t len;
|
||||
|
||||
if (cs->c == NULL)
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE)
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
buf = window_copy_get_selection(wme, &len);
|
||||
if (buf != NULL) {
|
||||
window_copy_open_buffer(cs, buf, len);
|
||||
free(buf);
|
||||
}
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_open_output(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
struct window_mode_entry *wme = cs->wme;
|
||||
void *buf;
|
||||
size_t len;
|
||||
|
||||
if (cs->c == NULL)
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
buf = window_copy_get_output(wme, &len, args_has(cs->wargs, 'a'));
|
||||
if (buf != NULL) {
|
||||
window_copy_open_buffer(cs, buf, len);
|
||||
free(buf);
|
||||
}
|
||||
return (WINDOW_COPY_CMD_NOTHING);
|
||||
}
|
||||
|
||||
static enum window_copy_cmd_action
|
||||
window_copy_cmd_goto_line(struct window_copy_cmd_state *cs)
|
||||
{
|
||||
@@ -3426,6 +3637,18 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_copy_pipe_and_cancel
|
||||
},
|
||||
{ .command = "copy-pipe-output",
|
||||
.args = { "aCP", 0, 2, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_copy_pipe_output
|
||||
},
|
||||
{ .command = "copy-output",
|
||||
.args = { "aCP", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_copy_output
|
||||
},
|
||||
{ .command = "copy-selection-no-clear",
|
||||
.args = { "CP", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
@@ -3648,6 +3871,18 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_EMACS_ONLY,
|
||||
.f = window_copy_cmd_other_end
|
||||
},
|
||||
{ .command = "open-output",
|
||||
.args = { "a", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
||||
.f = window_copy_cmd_open_output
|
||||
},
|
||||
{ .command = "open-selection",
|
||||
.args = { "", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_NEVER,
|
||||
.f = window_copy_cmd_open_selection
|
||||
},
|
||||
{ .command = "page-down",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
||||
@@ -3684,6 +3919,18 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_pipe_and_cancel
|
||||
},
|
||||
{ .command = "pipe-output",
|
||||
.args = { "a", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_pipe_output
|
||||
},
|
||||
{ .command = "pipe-selection",
|
||||
.args = { "", 0, 1, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_pipe
|
||||
},
|
||||
{ .command = "previous-matching-bracket",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = WINDOW_COPY_CMD_FLAG_READONLY,
|
||||
@@ -3861,6 +4108,12 @@ static const struct {
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_search_reverse
|
||||
},
|
||||
{ .command = "select-output",
|
||||
.args = { "a", 0, 0, NULL },
|
||||
.flags = 0,
|
||||
.clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,
|
||||
.f = window_copy_cmd_select_output
|
||||
},
|
||||
{ .command = "select-line",
|
||||
.args = { "", 0, 0, NULL },
|
||||
.flags = 0,
|
||||
@@ -6054,9 +6307,11 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
|
||||
window_copy_redraw_lines(wme, cy, sy - cy + 1);
|
||||
} else {
|
||||
if (endsy < cy) {
|
||||
window_copy_redraw_lines(wme, endsy, cy - endsy + 1);
|
||||
window_copy_redraw_lines(wme, endsy,
|
||||
cy - endsy + 1);
|
||||
} else {
|
||||
window_copy_redraw_lines(wme, cy, endsy - cy + 1);
|
||||
window_copy_redraw_lines(wme, cy,
|
||||
endsy - cy + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6064,6 +6319,78 @@ window_copy_set_selection(struct window_mode_entry *wme, int may_redraw,
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Get the range of the current output, or the whole buffer with all. */
|
||||
static int
|
||||
window_copy_get_output_range(struct window_mode_entry *wme, int all,
|
||||
u_int *sx, u_int *sy, u_int *ex, u_int *ey)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd = data->backing->grid;
|
||||
struct grid_reader gr;
|
||||
u_int total, cursor_y;
|
||||
int found;
|
||||
|
||||
if (!all) {
|
||||
cursor_y = screen_hsize(data->backing) + data->cy - data->oy;
|
||||
grid_reader_start(&gr, gd, data->cx, cursor_y);
|
||||
found = grid_reader_output_range(&gr, sx, sy, ex, ey);
|
||||
return (found);
|
||||
}
|
||||
total = gd->hsize + gd->sy;
|
||||
*sx = *sy = 0;
|
||||
*ey = total - 1;
|
||||
*ex = grid_get_line(gd, *ey)->cellused;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Get current output. */
|
||||
static void *
|
||||
window_copy_get_output(struct window_mode_entry *wme, size_t *len, int all)
|
||||
{
|
||||
void *buf;
|
||||
u_int sx, sy, ex, ey;
|
||||
|
||||
if (!window_copy_get_output_range(wme, all, &sx, &sy, &ex, &ey)) {
|
||||
*len = 0;
|
||||
return (NULL);
|
||||
}
|
||||
buf = window_copy_get_grid_range(wme, sx, sy, ex, ey, len);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
/* Get the text from sx,sy up to ex,ey, or NULL if there is none. */
|
||||
static void *
|
||||
window_copy_get_grid_range(struct window_mode_entry *wme, u_int sx, u_int sy,
|
||||
u_int ex, u_int ey, size_t *len)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd = data->backing->grid;
|
||||
char *buf;
|
||||
size_t off;
|
||||
u_int i, first, last;
|
||||
|
||||
buf = xmalloc(1);
|
||||
off = 0;
|
||||
for (i = sy; i <= ey; i++) {
|
||||
first = 0;
|
||||
last = gd->sx;
|
||||
if (i == sy)
|
||||
first = sx;
|
||||
if (i == ey)
|
||||
last = ex;
|
||||
window_copy_copy_line(wme, &buf, &off, i, first, last);
|
||||
}
|
||||
if (off != 0 && buf[off - 1] == '\n')
|
||||
off--;
|
||||
if (off == 0) {
|
||||
free(buf);
|
||||
*len = 0;
|
||||
return (NULL);
|
||||
}
|
||||
*len = off;
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void *
|
||||
window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
|
||||
{
|
||||
@@ -6074,7 +6401,7 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
|
||||
size_t off;
|
||||
u_int i, xx, yy, sx, sy, ex, ey, ey_last;
|
||||
u_int firstsx, lastex, restex, restsx, selx;
|
||||
int keys;
|
||||
int keys, wrapped;
|
||||
|
||||
if (data->screen.sel == NULL && data->lineflag == LINE_SEL_NONE) {
|
||||
buf = window_copy_match_at_cursor(data);
|
||||
@@ -6107,7 +6434,7 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
|
||||
}
|
||||
|
||||
/* Trim ex to end of line. */
|
||||
ey_last = window_copy_find_length(wme, ey);
|
||||
ey_last = window_copy_copy_line_length(wme, ey, &wrapped);
|
||||
if (ex > ey_last)
|
||||
ex = ey_last;
|
||||
|
||||
@@ -6178,8 +6505,7 @@ window_copy_get_selection(struct window_mode_entry *wme, size_t *len)
|
||||
}
|
||||
/* Remove final \n (unless at end in vi mode). */
|
||||
if (keys == MODEKEY_EMACS || lastex <= ey_last) {
|
||||
if (~grid_get_line(data->backing->grid, ey)->flags &
|
||||
GRID_LINE_WRAPPED || lastex != ey_last)
|
||||
if (!wrapped || lastex != ey_last)
|
||||
off -= 1;
|
||||
}
|
||||
*len = off;
|
||||
@@ -6220,18 +6546,28 @@ window_copy_pipe_run(struct window_mode_entry *wme, struct session *s,
|
||||
const char *cmd, size_t *len)
|
||||
{
|
||||
void *buf;
|
||||
struct job *job;
|
||||
|
||||
buf = window_copy_get_selection(wme, len);
|
||||
window_copy_pipe_buffer(s, cmd, buf, *len);
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_pipe_buffer(struct session *s, const char *cmd, void *buf,
|
||||
size_t len)
|
||||
{
|
||||
struct job *job;
|
||||
|
||||
if (buf == NULL)
|
||||
return;
|
||||
if (cmd == NULL || *cmd == '\0')
|
||||
cmd = options_get_string(global_options, "copy-command");
|
||||
if (cmd != NULL && *cmd != '\0') {
|
||||
job = job_run(cmd, 0, NULL, NULL, s, NULL, NULL, NULL, NULL,
|
||||
NULL, JOB_NOWAIT, -1, -1);
|
||||
if (job != NULL)
|
||||
bufferevent_write(job_get_event(job), buf, *len);
|
||||
bufferevent_write(job_get_event(job), buf, len);
|
||||
}
|
||||
return (buf);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -6307,6 +6643,29 @@ window_copy_append_selection(struct window_mode_entry *wme)
|
||||
free(bufname);
|
||||
}
|
||||
|
||||
/* Get the length for copying, preserving spaces on wrapped lines. */
|
||||
static u_int
|
||||
window_copy_copy_line_length(struct window_mode_entry *wme, u_int sy,
|
||||
int *wrapped)
|
||||
{
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd = data->backing->grid;
|
||||
struct grid_line *gl = grid_get_line(gd, sy);
|
||||
u_int length;
|
||||
int line_wrapped;
|
||||
|
||||
line_wrapped = 0;
|
||||
if ((gl->flags & GRID_LINE_WRAPPED) && gl->cellsize <= gd->sx)
|
||||
line_wrapped = 1;
|
||||
if (wrapped != NULL)
|
||||
*wrapped = line_wrapped;
|
||||
if (line_wrapped)
|
||||
length = gl->cellsize;
|
||||
else
|
||||
length = window_copy_find_length(wme, sy);
|
||||
return (length);
|
||||
}
|
||||
|
||||
static void
|
||||
window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
|
||||
u_int sy, u_int sx, u_int ex)
|
||||
@@ -6314,27 +6673,15 @@ window_copy_copy_line(struct window_mode_entry *wme, char **buf, size_t *off,
|
||||
struct window_copy_mode_data *data = wme->data;
|
||||
struct grid *gd = data->backing->grid;
|
||||
struct grid_cell gc;
|
||||
struct grid_line *gl;
|
||||
struct utf8_data ud;
|
||||
u_int i, xx, wrapped = 0;
|
||||
u_int i, xx;
|
||||
const char *s;
|
||||
int wrapped;
|
||||
|
||||
if (sx > ex)
|
||||
return;
|
||||
|
||||
/*
|
||||
* Work out if the line was wrapped at the screen edge and all of it is
|
||||
* on screen.
|
||||
*/
|
||||
gl = grid_get_line(gd, sy);
|
||||
if (gl->flags & GRID_LINE_WRAPPED && gl->cellsize <= gd->sx)
|
||||
wrapped = 1;
|
||||
|
||||
/* If the line was wrapped, don't strip spaces (use the full length). */
|
||||
if (wrapped)
|
||||
xx = gl->cellsize;
|
||||
else
|
||||
xx = window_copy_find_length(wme, sy);
|
||||
xx = window_copy_copy_line_length(wme, sy, &wrapped);
|
||||
if (ex > xx)
|
||||
ex = xx;
|
||||
if (sx > xx)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: window-customize.c,v 1.36 2026/07/28 10:35:31 nicm Exp $ */
|
||||
/* $OpenBSD: window-customize.c,v 1.37 2026/10/08 07:50:05 nicm Exp $ */
|
||||
|
||||
/*
|
||||
* Copyright (c) 2020 Nicholas Marriott <nicholas.marriott@gmail.com>
|
||||
@@ -2011,13 +2011,15 @@ window_customize_edit_close_cb(char *buf, size_t len, void *arg)
|
||||
switch (ed->edit_type) {
|
||||
case WINDOW_CUSTOMIZE_EDIT_OPTION:
|
||||
if (window_customize_option_editable(data, item) &&
|
||||
window_customize_set_option_value(item, value, &cause) != 0) {
|
||||
window_customize_set_option_value(item, value,
|
||||
&cause) != 0) {
|
||||
free(cause);
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case WINDOW_CUSTOMIZE_EDIT_KEY_COMMAND:
|
||||
if (window_customize_set_command_value(item, value, &cause) != 0) {
|
||||
if (window_customize_set_command_value(item, value,
|
||||
&cause) != 0) {
|
||||
free(cause);
|
||||
goto out;
|
||||
}
|
||||
@@ -2104,8 +2106,8 @@ window_customize_start_edit(struct window_customize_modedata *data,
|
||||
buf = "\n";
|
||||
len = 1;
|
||||
}
|
||||
ed->editor = spawn_editor(c, buf, len, window_customize_edit_close_cb,
|
||||
ed);
|
||||
ed->editor = spawn_editor(c, buf, len, NULL,
|
||||
window_customize_edit_close_cb, ed);
|
||||
free(value);
|
||||
if (ed->editor == NULL)
|
||||
window_customize_finish_edit(ed);
|
||||
|
||||
Reference in New Issue
Block a user