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>
@@ -18,6 +18,9 @@
#include <sys/types.h>
#include <stdlib.h>
#include <string.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);
static enum cmd_retval cmd_choose_tree_exec(struct cmd *,
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 = {
.name = "choose-tree",
@@ -93,9 +99,9 @@ const struct cmd_entry cmd_switch_mode_entry = {
.description = "Switch the active pane mode.",
.alias = NULL,
.args = { "F:kO:rst:wZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-krsZw] [-F format] [-O sort-order] " CMD_TARGET_PANE_USAGE
" [command]",
.args = { "F:f:kO:rst:wZ", 0, 1, cmd_choose_tree_args_parse },
.usage = "[-krsZw] [-F format] [-f path] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [command]",
.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 cmd_find_state *target = cmdq_get_target(item);
struct client *c = cmdq_get_client(item);
struct window_pane *wp = target->wp;
const struct window_mode *mode;
enum sort_order order;
char *path;
order = sort_order_from_string(args_get(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
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);
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>
.\"
@@ -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 14 2026 $
.Dd $Mdocdate: October 15 2026 $
.Dt TMUX 1
.Os
.Sh NAME
@@ -3205,6 +3205,7 @@ command works only if at least one client is attached.
.Ic switch\-mode
.Op Fl krsZw
.Op Fl F Ar format
.Op Fl f Ar path
.Op Fl O Ar sort\-order
.Op Fl t Ar target\-pane
.Op Ar command
@@ -3250,6 +3251,24 @@ If
is not given, "switch\-client \-Zt \[aq]%%\[aq]" is used.
.Fl F
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
kills the pane when the mode is exited.
.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 "socket_path" Ta "" Ta "Server socket path"
.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 "uid" Ta "" Ta "Server UID"
.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>
@@ -3962,6 +3962,7 @@ extern const struct window_mode window_tree_mode;
/* window-switch.c */
extern const struct window_mode window_switch_mode;
void window_switch_set_input(struct window_mode_entry *, struct evbuffer *);
/* window-clock.c */
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>
@@ -63,7 +63,8 @@ const struct window_mode window_switch_mode = {
enum window_switch_type {
WINDOW_SWITCH_TYPE_SESSION,
WINDOW_SWITCH_TYPE_WINDOW
WINDOW_SWITCH_TYPE_WINDOW,
WINDOW_SWITCH_TYPE_INPUT
};
struct window_switch_itemdata {
@@ -73,6 +74,7 @@ struct window_switch_itemdata {
uint64_t tag;
char *text;
const char *value;
bitstr_t *match;
u_int score;
@@ -87,6 +89,9 @@ struct window_switch_modedata {
char *format;
char *command;
char **lines;
u_int lines_size;
enum window_switch_type type;
enum sort_order order;
int reversed;
@@ -165,6 +170,20 @@ window_switch_add_window(struct window_switch_modedata *data,
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
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 winlink **wl;
struct sort_criteria sort_crit;
struct cmd_find_state fs;
struct format_tree *ft;
sort_crit.order = data->order;
sort_crit.reversed = data->reversed;
@@ -214,6 +235,14 @@ window_switch_build(struct window_switch_modedata *data)
for (i = 0; i < nw; i++)
window_switch_add_window(data, wl[i], &order);
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++) {
@@ -365,11 +394,16 @@ window_switch_init(struct window_mode_entry *wme,
data->type = WINDOW_SWITCH_TYPE_WINDOW;
else
data->type = WINDOW_SWITCH_TYPE_SESSION;
if (args_has(args, 'f'))
data->type = WINDOW_SWITCH_TYPE_INPUT;
data->filter = xstrdup("");
if (args == NULL || !args_has(args, 'F'))
data->format = xstrdup(WINDOW_SWITCH_DEFAULT_FORMAT);
else
if (args == NULL || !args_has(args, 'F')) {
if (data->type == WINDOW_SWITCH_TYPE_INPUT)
data->format = xstrdup("#{switch_item}");
else
data->format = xstrdup(WINDOW_SWITCH_DEFAULT_FORMAT);
} else
data->format = xstrdup(args_get(args, 'F'));
if (args == NULL || args_count(args) == 0)
data->command = xstrdup(WINDOW_SWITCH_DEFAULT_COMMAND);
@@ -407,6 +441,36 @@ window_switch_init(struct window_mode_entry *wme,
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
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++)
window_switch_free_item(data->item_list[i]);
free(data->item_list);
for (i = 0; i < data->lines_size; i++)
free(data->lines[i]);
free(data->lines);
free(data->matches);
free(data->filter);
@@ -477,6 +544,10 @@ window_switch_run_command(struct window_switch_modedata *data, struct client *c)
}
}
break;
case WINDOW_SWITCH_TYPE_INPUT:
target = xstrdup(item->value);
cmd_find_from_pane(&fs, data->wp, 0);
break;
}
if (target == NULL)
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);
if (command != NULL && *command != '\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);
if (status == CMD_PARSE_ERROR) {
if (c != NULL) {
@@ -539,7 +612,8 @@ window_switch_key(struct window_mode_entry *wme, struct client *c,
return;
if (data->prompt != NULL && screen_size_y(&data->screen) != 0 &&
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)) {
result = prompt_mouse(data->prompt, x, 0,
screen_size_x(&data->screen), &redraw);