Add -f to switch-mode to read choices from a file, GitHub issue 5738.

This commit is contained in:
nicm
2026-10-15 08:07:17 +00:00
committed by tmux update bot
parent 1fbadf9b41
commit 73d8f81c2c
4 changed files with 163 additions and 13 deletions

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: cmd-choose-tree.c,v 1.60 2026/10/09 13:12:14 nicm Exp $ */ /* $OpenBSD: cmd-choose-tree.c,v 1.61 2026/10/15 08:07:17 nicm Exp $ */
/* /*
* Copyright (c) 2012 Thomas Adam <thomas@xteddy.org> * Copyright (c) 2012 Thomas Adam <thomas@xteddy.org>
@@ -18,6 +18,9 @@
#include <sys/types.h> #include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h" #include "tmux.h"
/* /*
@@ -28,6 +31,9 @@ static enum args_parse_type cmd_choose_tree_args_parse(struct args *args,
u_int idx, char **cause); u_int idx, char **cause);
static enum cmd_retval cmd_choose_tree_exec(struct cmd *, static enum cmd_retval cmd_choose_tree_exec(struct cmd *,
struct cmdq_item *); struct cmdq_item *);
static void cmd_choose_tree_read_done(struct client *,
const char *, int, int, struct evbuffer *,
void *);
const struct cmd_entry cmd_choose_tree_entry = { const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree", .name = "choose-tree",
@@ -93,9 +99,9 @@ const struct cmd_entry cmd_switch_mode_entry = {
.description = "Switch the active pane mode.", .description = "Switch the active pane mode.",
.alias = NULL, .alias = NULL,
.args = { "F:kO:rst:wZ", 0, 1, cmd_choose_tree_args_parse }, .args = { "F:f:kO:rst:wZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-krsZw] [-F format] [-O sort-order] " CMD_TARGET_PANE_USAGE .usage = "[-krsZw] [-F format] [-f path] [-O sort-order] "
" [command]", CMD_TARGET_PANE_USAGE " [command]",
.target = { 't', CMD_FIND_PANE, 0 }, .target = { 't', CMD_FIND_PANE, 0 },
@@ -131,9 +137,11 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
{ {
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
struct client *c = cmdq_get_client(item);
struct window_pane *wp = target->wp; struct window_pane *wp = target->wp;
const struct window_mode *mode; const struct window_mode *mode;
enum sort_order order; enum sort_order order;
char *path;
order = sort_order_from_string(args_get(args, 'O')); order = sort_order_from_string(args_get(args, 'O'));
if (order == SORT_END && args_has(args, 'O')) { if (order == SORT_END && args_has(args, 'O')) {
@@ -158,6 +166,53 @@ cmd_choose_tree_exec(struct cmd *self, struct cmdq_item *item)
else else
mode = &window_tree_mode; mode = &window_tree_mode;
if (mode == &window_switch_mode && args_has(args, 'f')) {
path = format_single_from_target(item, args_get(args, 'f'));
file_read(c, path, cmd_choose_tree_read_done, item);
free(path);
return (CMD_RETURN_WAIT);
}
window_pane_set_mode(wp, NULL, mode, item, target, args); window_pane_set_mode(wp, NULL, mode, item, target, args);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);
} }
static void
cmd_choose_tree_read_done(__unused struct client *c, const char *path,
int error, int closed, struct evbuffer *buffer, void *data)
{
struct cmdq_item *item = data;
struct cmd *cmd = cmdq_get_cmd(item);
struct args *args = cmd_get_args(cmd);
struct cmd_find_state *target = cmdq_get_target(item);
struct window_pane *wp = target->wp;
struct window_mode_entry *wme;
char *buf = EVBUFFER_DATA(buffer);
size_t len = EVBUFFER_LENGTH(buffer);
if (!closed)
return;
if (error != 0) {
cmdq_error(item, "%s: %s", strerror(error), path);
goto out;
}
if (len != 0 && memchr(buf, '\0', len) != NULL) {
cmdq_error(item, "%s: contains binary data", path);
goto out;
}
if (!cmd_find_valid_state(target)) {
cmdq_error(item, "target pane no longer exists");
goto out;
}
TAILQ_FOREACH(wme, &wp->modes, entry) {
if (wme->mode == &window_switch_mode)
break;
}
if (window_pane_set_mode(wp, NULL, &window_switch_mode, item, target,
args) == 0 && wme == NULL)
window_switch_set_input(TAILQ_FIRST(&wp->modes), buffer);
out:
cmdq_continue(item);
}

24
tmux.1
View File

@@ -1,4 +1,4 @@
.\" $OpenBSD: tmux.1,v 1.1186 2026/10/14 12:33:46 nicm Exp $ .\" $OpenBSD: tmux.1,v 1.1187 2026/10/15 08:07:17 nicm Exp $
.\" .\"
.\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> .\" Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
.\" .\"
@@ -14,7 +14,7 @@
.\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING .\" IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
.\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
.\" .\"
.Dd $Mdocdate: October 14 2026 $ .Dd $Mdocdate: October 15 2026 $
.Dt TMUX 1 .Dt TMUX 1
.Os .Os
.Sh NAME .Sh NAME
@@ -3205,6 +3205,7 @@ command works only if at least one client is attached.
.Ic switch\-mode .Ic switch\-mode
.Op Fl krsZw .Op Fl krsZw
.Op Fl F Ar format .Op Fl F Ar format
.Op Fl f Ar path
.Op Fl O Ar sort\-order .Op Fl O Ar sort\-order
.Op Fl t Ar target\-pane .Op Fl t Ar target\-pane
.Op Ar command .Op Ar command
@@ -3250,6 +3251,24 @@ If
is not given, "switch\-client \-Zt \[aq]%%\[aq]" is used. is not given, "switch\-client \-Zt \[aq]%%\[aq]" is used.
.Fl F .Fl F
specifies the format for each item in the list. specifies the format for each item in the list.
.Pp
.Fl f
reads choices from
.Ar path
instead of listing sessions or windows.
Each line is one choice.
With
.Fl f ,
.Ql switch_item
contains the original line and the default display format is
.Ql #{switch_item} .
Fuzzy matching uses the displayed text.
When a choice is selected,
.Ql %%
and
.Ql %1
are replaced by the original line
.Pp
.Fl k .Fl k
kills the pane when the mode is exited. kills the pane when the mode is exited.
.Pp .Pp
@@ -7619,6 +7638,7 @@ The following variables are available, where appropriate:
.It Li "session_windows" Ta "" Ta "Number of windows in session" .It Li "session_windows" Ta "" Ta "Number of windows in session"
.It Li "socket_path" Ta "" Ta "Server socket path" .It Li "socket_path" Ta "" Ta "Server socket path"
.It Li "start_time" Ta "" Ta "Server start time" .It Li "start_time" Ta "" Ta "Server start time"
.It Li "switch_item" Ta "" Ta "Original input line in switch mode"
.It Li "synchronized_output_flag" Ta "" Ta "1 if pane has synchronized output enabled" .It Li "synchronized_output_flag" Ta "" Ta "1 if pane has synchronized output enabled"
.It Li "uid" Ta "" Ta "Server UID" .It Li "uid" Ta "" Ta "Server UID"
.It Li "user" Ta "" Ta "Server user" .It Li "user" Ta "" Ta "Server user"

3
tmux.h
View File

@@ -1,4 +1,4 @@
/* $OpenBSD: tmux.h,v 1.1463 2026/10/14 12:48:50 nicm Exp $ */ /* $OpenBSD: tmux.h,v 1.1464 2026/10/15 08:07:17 nicm Exp $ */
/* /*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -3962,6 +3962,7 @@ extern const struct window_mode window_tree_mode;
/* window-switch.c */ /* window-switch.c */
extern const struct window_mode window_switch_mode; extern const struct window_mode window_switch_mode;
void window_switch_set_input(struct window_mode_entry *, struct evbuffer *);
/* window-clock.c */ /* window-clock.c */
extern const struct window_mode window_clock_mode; extern const struct window_mode window_clock_mode;

View File

@@ -1,4 +1,4 @@
/* $OpenBSD: window-switch.c,v 1.5 2026/10/07 13:13:49 nicm Exp $ */ /* $OpenBSD: window-switch.c,v 1.6 2026/10/15 08:07:17 nicm Exp $ */
/* /*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com> * Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.com>
@@ -63,7 +63,8 @@ const struct window_mode window_switch_mode = {
enum window_switch_type { enum window_switch_type {
WINDOW_SWITCH_TYPE_SESSION, WINDOW_SWITCH_TYPE_SESSION,
WINDOW_SWITCH_TYPE_WINDOW WINDOW_SWITCH_TYPE_WINDOW,
WINDOW_SWITCH_TYPE_INPUT
}; };
struct window_switch_itemdata { struct window_switch_itemdata {
@@ -73,6 +74,7 @@ struct window_switch_itemdata {
uint64_t tag; uint64_t tag;
char *text; char *text;
const char *value;
bitstr_t *match; bitstr_t *match;
u_int score; u_int score;
@@ -87,6 +89,9 @@ struct window_switch_modedata {
char *format; char *format;
char *command; char *command;
char **lines;
u_int lines_size;
enum window_switch_type type; enum window_switch_type type;
enum sort_order order; enum sort_order order;
int reversed; int reversed;
@@ -165,6 +170,20 @@ window_switch_add_window(struct window_switch_modedata *data,
format_free(ft); format_free(ft);
} }
static void
window_switch_add_input(struct window_switch_modedata *data, u_int idx,
struct format_tree *ft)
{
struct window_switch_itemdata *item;
item = window_switch_add_item(data);
item->type = WINDOW_SWITCH_TYPE_INPUT;
item->value = data->lines[idx];
item->order = data->reversed ? data->lines_size - idx - 1 : idx;
format_add(ft, "switch_item", "%s", item->value);
item->text = format_expand(ft, data->format);
}
static int static int
window_switch_compare(const void *a0, const void *b0) window_switch_compare(const void *a0, const void *b0)
{ {
@@ -192,6 +211,8 @@ window_switch_build(struct window_switch_modedata *data)
struct session **sl; struct session **sl;
struct winlink **wl; struct winlink **wl;
struct sort_criteria sort_crit; struct sort_criteria sort_crit;
struct cmd_find_state fs;
struct format_tree *ft;
sort_crit.order = data->order; sort_crit.order = data->order;
sort_crit.reversed = data->reversed; sort_crit.reversed = data->reversed;
@@ -214,6 +235,14 @@ window_switch_build(struct window_switch_modedata *data)
for (i = 0; i < nw; i++) for (i = 0; i < nw; i++)
window_switch_add_window(data, wl[i], &order); window_switch_add_window(data, wl[i], &order);
break; break;
case WINDOW_SWITCH_TYPE_INPUT:
cmd_find_from_pane(&fs, data->wp, 0);
ft = format_create(NULL, NULL, FORMAT_NONE, 0);
format_defaults(ft, NULL, fs.s, fs.wl, fs.wp);
for (i = 0; i < data->lines_size; i++)
window_switch_add_input(data, i, ft);
format_free(ft);
break;
} }
for (i = 0; i < data->item_size; i++) { for (i = 0; i < data->item_size; i++) {
@@ -365,11 +394,16 @@ window_switch_init(struct window_mode_entry *wme,
data->type = WINDOW_SWITCH_TYPE_WINDOW; data->type = WINDOW_SWITCH_TYPE_WINDOW;
else else
data->type = WINDOW_SWITCH_TYPE_SESSION; data->type = WINDOW_SWITCH_TYPE_SESSION;
if (args_has(args, 'f'))
data->type = WINDOW_SWITCH_TYPE_INPUT;
data->filter = xstrdup(""); data->filter = xstrdup("");
if (args == NULL || !args_has(args, 'F')) if (args == NULL || !args_has(args, 'F')) {
data->format = xstrdup(WINDOW_SWITCH_DEFAULT_FORMAT); if (data->type == WINDOW_SWITCH_TYPE_INPUT)
else data->format = xstrdup("#{switch_item}");
else
data->format = xstrdup(WINDOW_SWITCH_DEFAULT_FORMAT);
} else
data->format = xstrdup(args_get(args, 'F')); data->format = xstrdup(args_get(args, 'F'));
if (args == NULL || args_count(args) == 0) if (args == NULL || args_count(args) == 0)
data->command = xstrdup(WINDOW_SWITCH_DEFAULT_COMMAND); data->command = xstrdup(WINDOW_SWITCH_DEFAULT_COMMAND);
@@ -407,6 +441,36 @@ window_switch_init(struct window_mode_entry *wme,
return (s); return (s);
} }
void
window_switch_set_input(struct window_mode_entry *wme, struct evbuffer *buffer)
{
struct window_switch_modedata *data = wme->data;
char *line;
size_t size;
for (;;) {
line = evbuffer_readln(buffer, NULL, EVBUFFER_EOL_LF);
if (line == NULL) {
size = EVBUFFER_LENGTH(buffer);
if (size == 0)
break;
line = xmemdup(EVBUFFER_DATA(buffer), size);
evbuffer_drain(buffer, size);
}
if (*line == '\0') {
free(line);
continue;
}
data->lines = xreallocarray(data->lines, data->lines_size + 1,
sizeof *data->lines);
data->lines[data->lines_size++] = line;
}
window_switch_build(data);
window_switch_set_current(data, 0);
window_switch_draw_screen(wme);
wme->wp->flags |= PANE_REDRAW;
}
static void static void
window_switch_free(struct window_mode_entry *wme) window_switch_free(struct window_mode_entry *wme)
{ {
@@ -419,6 +483,9 @@ window_switch_free(struct window_mode_entry *wme)
for (i = 0; i < data->item_size; i++) for (i = 0; i < data->item_size; i++)
window_switch_free_item(data->item_list[i]); window_switch_free_item(data->item_list[i]);
free(data->item_list); free(data->item_list);
for (i = 0; i < data->lines_size; i++)
free(data->lines[i]);
free(data->lines);
free(data->matches); free(data->matches);
free(data->filter); free(data->filter);
@@ -477,6 +544,10 @@ window_switch_run_command(struct window_switch_modedata *data, struct client *c)
} }
} }
break; break;
case WINDOW_SWITCH_TYPE_INPUT:
target = xstrdup(item->value);
cmd_find_from_pane(&fs, data->wp, 0);
break;
} }
if (target == NULL) if (target == NULL)
return (0); return (0);
@@ -484,6 +555,8 @@ window_switch_run_command(struct window_switch_modedata *data, struct client *c)
command = cmd_template_replace(data->command, target, 1); command = cmd_template_replace(data->command, target, 1);
if (command != NULL && *command != '\0') { if (command != NULL && *command != '\0') {
state = cmdq_new_state(&fs, NULL, 0); state = cmdq_new_state(&fs, NULL, 0);
if (item->type == WINDOW_SWITCH_TYPE_INPUT)
cmdq_add_format(state, "switch_item", "%s", item->value);
status = cmd_parse_and_append(command, NULL, c, state, &error); status = cmd_parse_and_append(command, NULL, c, state, &error);
if (status == CMD_PARSE_ERROR) { if (status == CMD_PARSE_ERROR) {
if (c != NULL) { if (c != NULL) {
@@ -539,7 +612,8 @@ window_switch_key(struct window_mode_entry *wme, struct client *c,
return; return;
if (data->prompt != NULL && screen_size_y(&data->screen) != 0 && if (data->prompt != NULL && screen_size_y(&data->screen) != 0 &&
y == screen_size_y(&data->screen) - 1 && y == screen_size_y(&data->screen) - 1 &&
MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_1 && !MOUSE_DRAG(m->b) && MOUSE_BUTTONS(m->b) == MOUSE_BUTTON_1 &&
!MOUSE_DRAG(m->b) &&
!MOUSE_RELEASE(m->b)) { !MOUSE_RELEASE(m->b)) {
result = prompt_mouse(data->prompt, x, 0, result = prompt_mouse(data->prompt, x, 0,
screen_size_x(&data->screen), &redraw); screen_size_x(&data->screen), &redraw);