mirror of
https://github.com/tmux/tmux.git
synced 2025-03-31 03:58:48 +00:00
Extend command-prompt with a -p option which is a comma-separated list of one
or more prompts to present in order. The responses to the prompt are replaced in the template string: %% are replaced in order, so the first prompt replaces the first %%, the second replaces the second, and so on. In addition, %1 up to %9 are replaced with the responses to the first the ninth prompts The default template is "%1" so the response to the first prompt is processed as a command. Note that this changes the behaviour for %% so if there is only one prompt, only the first %% will be replaced. Templates such as "neww -n '%%' 'ssh %%'" should be changed to "neww -n '%1' 'ssh %1'". From Tiago Cunha.
This commit is contained in:
parent
036de0c5e4
commit
3f4418d84d
@ -27,56 +27,112 @@
|
|||||||
* Prompt for command in client.
|
* Prompt for command in client.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void cmd_command_prompt_init(struct cmd *, int);
|
void cmd_command_prompt_init(struct cmd *, int);
|
||||||
int cmd_command_prompt_exec(struct cmd *, struct cmd_ctx *);
|
int cmd_command_prompt_parse(struct cmd *, int, char **, char **);
|
||||||
|
int cmd_command_prompt_exec(struct cmd *, struct cmd_ctx *);
|
||||||
|
void cmd_command_prompt_free(struct cmd *);
|
||||||
|
size_t cmd_command_prompt_print(struct cmd *, char *, size_t);
|
||||||
|
|
||||||
int cmd_command_prompt_callback(void *, const char *);
|
int cmd_command_prompt_callback(void *, const char *);
|
||||||
void cmd_command_prompt_free(void *);
|
void cmd_command_prompt_cfree(void *);
|
||||||
|
char *cmd_command_prompt_replace(char *, const char *, int);
|
||||||
|
|
||||||
const struct cmd_entry cmd_command_prompt_entry = {
|
const struct cmd_entry cmd_command_prompt_entry = {
|
||||||
"command-prompt", NULL,
|
"command-prompt", NULL,
|
||||||
CMD_TARGET_CLIENT_USAGE " [template]",
|
CMD_TARGET_CLIENT_USAGE " [-p prompts] [template]",
|
||||||
CMD_ARG01, 0,
|
0, 0,
|
||||||
cmd_command_prompt_init,
|
cmd_command_prompt_init,
|
||||||
cmd_target_parse,
|
cmd_command_prompt_parse,
|
||||||
cmd_command_prompt_exec,
|
cmd_command_prompt_exec,
|
||||||
cmd_target_free,
|
cmd_command_prompt_free,
|
||||||
cmd_target_print
|
cmd_command_prompt_print
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cmd_command_prompt_data {
|
struct cmd_command_prompt_data {
|
||||||
|
char *prompts;
|
||||||
|
char *target;
|
||||||
|
char *template;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct cmd_command_prompt_cdata {
|
||||||
struct client *c;
|
struct client *c;
|
||||||
|
char *next_prompt;
|
||||||
|
char *prompts;
|
||||||
char *template;
|
char *template;
|
||||||
|
int idx;
|
||||||
};
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
cmd_command_prompt_init(struct cmd *self, int key)
|
cmd_command_prompt_init(struct cmd *self, int key)
|
||||||
{
|
{
|
||||||
struct cmd_target_data *data;
|
struct cmd_command_prompt_data *data;
|
||||||
|
|
||||||
cmd_target_init(self, key);
|
self->data = data = xmalloc(sizeof *data);
|
||||||
data = self->data;
|
data->prompts = NULL;
|
||||||
|
data->target = NULL;
|
||||||
|
data->template = NULL;
|
||||||
|
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case ',':
|
case ',':
|
||||||
data->arg = xstrdup("rename-window '%%'");
|
data->template = xstrdup("rename-window '%%'");
|
||||||
break;
|
break;
|
||||||
case '.':
|
case '.':
|
||||||
data->arg = xstrdup("move-window -t '%%'");
|
data->template = xstrdup("move-window -t '%%'");
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
data->arg = xstrdup("find-window '%%'");
|
data->template = xstrdup("find-window '%%'");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
cmd_command_prompt_parse(struct cmd *self, int argc, char **argv, char **cause)
|
||||||
|
{
|
||||||
|
struct cmd_command_prompt_data *data;
|
||||||
|
int opt;
|
||||||
|
|
||||||
|
self->entry->init(self, 0);
|
||||||
|
data = self->data;
|
||||||
|
|
||||||
|
while ((opt = getopt(argc, argv, "p:t:")) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'p':
|
||||||
|
if (data->prompts == NULL)
|
||||||
|
data->prompts = xstrdup(optarg);
|
||||||
|
break;
|
||||||
|
case 't':
|
||||||
|
if (data->target == NULL)
|
||||||
|
data->target = xstrdup(optarg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
goto usage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argc -= optind;
|
||||||
|
argv += optind;
|
||||||
|
if (argc != 0 && argc != 1)
|
||||||
|
goto usage;
|
||||||
|
|
||||||
|
if (argc == 1)
|
||||||
|
data->template = xstrdup(argv[0]);
|
||||||
|
|
||||||
|
return (0);
|
||||||
|
|
||||||
|
usage:
|
||||||
|
xasprintf(cause, "usage: %s %s", self->entry->name, self->entry->usage);
|
||||||
|
|
||||||
|
self->entry->free(self);
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx)
|
cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx)
|
||||||
{
|
{
|
||||||
struct cmd_target_data *data = self->data;
|
struct cmd_command_prompt_data *data = self->data;
|
||||||
struct cmd_command_prompt_data *cdata;
|
struct cmd_command_prompt_cdata *cdata;
|
||||||
struct client *c;
|
struct client *c;
|
||||||
char *hdr, *ptr;
|
char *prompt, *ptr;
|
||||||
|
size_t n;
|
||||||
|
|
||||||
if ((c = cmd_find_client(ctx, data->target)) == NULL)
|
if ((c = cmd_find_client(ctx, data->target)) == NULL)
|
||||||
return (-1);
|
return (-1);
|
||||||
@ -86,76 +142,100 @@ cmd_command_prompt_exec(struct cmd *self, struct cmd_ctx *ctx)
|
|||||||
|
|
||||||
cdata = xmalloc(sizeof *cdata);
|
cdata = xmalloc(sizeof *cdata);
|
||||||
cdata->c = c;
|
cdata->c = c;
|
||||||
if (data->arg != NULL) {
|
cdata->idx = 1;
|
||||||
cdata->template = xstrdup(data->arg);
|
cdata->next_prompt = NULL;
|
||||||
if ((ptr = strchr(data->arg, ' ')) == NULL)
|
cdata->prompts = NULL;
|
||||||
ptr = strchr(data->arg, '\0');
|
cdata->template = NULL;
|
||||||
xasprintf(&hdr, "(%.*s) ", (int) (ptr - data->arg), data->arg);
|
|
||||||
} else {
|
if (data->template != NULL)
|
||||||
cdata->template = NULL;
|
cdata->template = xstrdup(data->template);
|
||||||
hdr = xstrdup(":");
|
else
|
||||||
}
|
cdata->template = xstrdup("%1");
|
||||||
status_prompt_set(c, hdr,
|
if (data->prompts != NULL)
|
||||||
cmd_command_prompt_callback, cmd_command_prompt_free, cdata, 0);
|
cdata->prompts = xstrdup(data->prompts);
|
||||||
xfree(hdr);
|
else if (data->template != NULL) {
|
||||||
|
n = strcspn(data->template, " ,");
|
||||||
|
xasprintf(&cdata->prompts, "(%.*s) ", (int) n, data->template);
|
||||||
|
} else
|
||||||
|
cdata->prompts = xstrdup(":");
|
||||||
|
|
||||||
|
cdata->next_prompt = cdata->prompts;
|
||||||
|
ptr = strsep(&cdata->next_prompt, ",");
|
||||||
|
if (data->prompts == NULL)
|
||||||
|
prompt = xstrdup(ptr);
|
||||||
|
else
|
||||||
|
xasprintf(&prompt, "%s ", ptr);
|
||||||
|
status_prompt_set(c, prompt, cmd_command_prompt_callback,
|
||||||
|
cmd_command_prompt_cfree, cdata, 0);
|
||||||
|
xfree(prompt);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
cmd_command_prompt_free(struct cmd *self)
|
||||||
|
{
|
||||||
|
struct cmd_command_prompt_data *data = self->data;
|
||||||
|
|
||||||
|
if (data->prompts != NULL)
|
||||||
|
xfree(data->prompts);
|
||||||
|
if (data->target != NULL)
|
||||||
|
xfree(data->target);
|
||||||
|
if (data->template != NULL)
|
||||||
|
xfree(data->template);
|
||||||
|
xfree(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t
|
||||||
|
cmd_command_prompt_print(struct cmd *self, char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct cmd_command_prompt_data *data = self->data;
|
||||||
|
size_t off = 0;
|
||||||
|
|
||||||
|
off += xsnprintf(buf, len, "%s", self->entry->name);
|
||||||
|
if (data == NULL)
|
||||||
|
return (off);
|
||||||
|
if (off < len && data->prompts != NULL)
|
||||||
|
off += cmd_prarg(buf + off, len - off, " -p ", data->prompts);
|
||||||
|
if (off < len && data->target != NULL)
|
||||||
|
off += cmd_prarg(buf + off, len - off, " -t ", data->target);
|
||||||
|
if (off < len && data->template != NULL)
|
||||||
|
off += cmd_prarg(buf + off, len - off, " ", data->template);
|
||||||
|
return (off);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
cmd_command_prompt_callback(void *data, const char *s)
|
cmd_command_prompt_callback(void *data, const char *s)
|
||||||
{
|
{
|
||||||
struct cmd_command_prompt_data *cdata = data;
|
struct cmd_command_prompt_cdata *cdata = data;
|
||||||
struct client *c = cdata->c;
|
struct client *c = cdata->c;
|
||||||
struct cmd_list *cmdlist;
|
struct cmd_list *cmdlist;
|
||||||
struct cmd_ctx ctx;
|
struct cmd_ctx ctx;
|
||||||
char *cause, *ptr, *buf, ch;
|
char *cause, *newtempl, *prompt, *ptr;
|
||||||
size_t len, slen;
|
|
||||||
|
|
||||||
if (s == NULL || *s == '\0')
|
if (s == NULL)
|
||||||
return (0);
|
return (0);
|
||||||
slen = strlen(s);
|
|
||||||
|
|
||||||
len = 0;
|
newtempl = cmd_command_prompt_replace(cdata->template, s, cdata->idx);
|
||||||
buf = NULL;
|
xfree(cdata->template);
|
||||||
if (cdata->template != NULL) {
|
cdata->template = newtempl;
|
||||||
ptr = cdata->template;
|
|
||||||
while (*ptr != '\0') {
|
|
||||||
switch (ch = *ptr++) {
|
|
||||||
case '%':
|
|
||||||
if (*ptr != '%')
|
|
||||||
break;
|
|
||||||
ptr++;
|
|
||||||
|
|
||||||
buf = xrealloc(buf, 1, len + slen + 1);
|
if ((ptr = strsep(&cdata->next_prompt, ",")) != NULL) {
|
||||||
memcpy(buf + len, s, slen);
|
xasprintf(&prompt, "%s ", ptr);
|
||||||
len += slen;
|
status_prompt_update(c, prompt);
|
||||||
break;
|
xfree(prompt);
|
||||||
default:
|
cdata->idx++;
|
||||||
buf = xrealloc(buf, 1, len + 2);
|
return (1);
|
||||||
buf[len++] = ch;
|
}
|
||||||
break;
|
|
||||||
}
|
if (cmd_string_parse(newtempl, &cmdlist, &cause) != 0) {
|
||||||
|
if (cause != NULL) {
|
||||||
|
*cause = toupper((u_char) *cause);
|
||||||
|
status_message_set(c, "%s", cause);
|
||||||
|
xfree(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (buf == NULL)
|
|
||||||
return (0);
|
|
||||||
buf[len] = '\0';
|
|
||||||
s = buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cmd_string_parse(s, &cmdlist, &cause) != 0) {
|
|
||||||
if (cause == NULL)
|
|
||||||
return (0);
|
|
||||||
*cause = toupper((u_char) *cause);
|
|
||||||
status_message_set(c, "%s", cause);
|
|
||||||
xfree(cause);
|
|
||||||
cmdlist = NULL;
|
|
||||||
}
|
|
||||||
if (buf != NULL)
|
|
||||||
xfree(buf);
|
|
||||||
if (cmdlist == NULL)
|
|
||||||
return (0);
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
ctx.msgdata = NULL;
|
ctx.msgdata = NULL;
|
||||||
ctx.cursession = c->session;
|
ctx.cursession = c->session;
|
||||||
@ -176,11 +256,53 @@ cmd_command_prompt_callback(void *data, const char *s)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cmd_command_prompt_free(void *data)
|
cmd_command_prompt_cfree(void *data)
|
||||||
{
|
{
|
||||||
struct cmd_command_prompt_data *cdata = data;
|
struct cmd_command_prompt_cdata *cdata = data;
|
||||||
|
|
||||||
|
if (cdata->prompts != NULL)
|
||||||
|
xfree(cdata->prompts);
|
||||||
if (cdata->template != NULL)
|
if (cdata->template != NULL)
|
||||||
xfree(cdata->template);
|
xfree(cdata->template);
|
||||||
xfree(cdata);
|
xfree(cdata);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
cmd_command_prompt_replace(char *template, const char *s, int idx)
|
||||||
|
{
|
||||||
|
char ch;
|
||||||
|
char *buf, *ptr;
|
||||||
|
int replaced;
|
||||||
|
size_t len;
|
||||||
|
|
||||||
|
if (strstr(template, "%") == NULL)
|
||||||
|
return (xstrdup(template));
|
||||||
|
|
||||||
|
buf = xmalloc(1);
|
||||||
|
*buf = '\0';
|
||||||
|
len = 0;
|
||||||
|
replaced = 0;
|
||||||
|
|
||||||
|
ptr = template;
|
||||||
|
while (*ptr != '\0') {
|
||||||
|
switch (ch = *ptr++) {
|
||||||
|
case '%':
|
||||||
|
if (*ptr < '1' || *ptr > '9' || *ptr - '0' != idx) {
|
||||||
|
if (*ptr != '%' || replaced)
|
||||||
|
break;
|
||||||
|
replaced = 1;
|
||||||
|
}
|
||||||
|
ptr++;
|
||||||
|
|
||||||
|
len += strlen(s);
|
||||||
|
buf = xrealloc(buf, 1, len + 1);
|
||||||
|
strlcat(buf, s, len + 1);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
buf = xrealloc(buf, 1, len + 2);
|
||||||
|
buf[len++] = ch;
|
||||||
|
buf[len] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
return (buf);
|
||||||
|
}
|
||||||
|
14
status.c
14
status.c
@ -668,6 +668,20 @@ status_prompt_clear(struct client *c)
|
|||||||
screen_reinit(&c->status);
|
screen_reinit(&c->status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
status_prompt_update(struct client *c, const char *msg)
|
||||||
|
{
|
||||||
|
xfree(c->prompt_string);
|
||||||
|
c->prompt_string = xstrdup(msg);
|
||||||
|
|
||||||
|
*c->prompt_buffer = '\0';
|
||||||
|
c->prompt_index = 0;
|
||||||
|
|
||||||
|
c->prompt_hindex = 0;
|
||||||
|
|
||||||
|
c->flags |= CLIENT_STATUS;
|
||||||
|
}
|
||||||
|
|
||||||
/* Draw client prompt on status line of present else on last line. */
|
/* Draw client prompt on status line of present else on last line. */
|
||||||
int
|
int
|
||||||
status_prompt_redraw(struct client *c)
|
status_prompt_redraw(struct client *c)
|
||||||
|
28
tmux.1
28
tmux.1
@ -1662,6 +1662,7 @@ session option.
|
|||||||
Commands related to the status line are as follows:
|
Commands related to the status line are as follows:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Xo Ic command-prompt
|
.It Xo Ic command-prompt
|
||||||
|
.Op Fl p Ar prompts
|
||||||
.Op Fl t Ar target-client
|
.Op Fl t Ar target-client
|
||||||
.Op Ar template
|
.Op Ar template
|
||||||
.Xc
|
.Xc
|
||||||
@ -1671,8 +1672,30 @@ This may be used from inside
|
|||||||
to execute commands interactively.
|
to execute commands interactively.
|
||||||
If
|
If
|
||||||
.Ar template
|
.Ar template
|
||||||
is specified, it is used as the command; any %% in the template will be
|
is specified, it is used as the command.
|
||||||
replaced by what is entered at the prompt.
|
If
|
||||||
|
.Fl p
|
||||||
|
is given,
|
||||||
|
.Ar prompts
|
||||||
|
is a comma-separated list of prompts which are displayed in order; otherwise
|
||||||
|
a single prompt is displayed, constructed from
|
||||||
|
.Ar template
|
||||||
|
if it is present, or
|
||||||
|
.Ql \&:
|
||||||
|
if not.
|
||||||
|
Before the command is executed, the first occurrence of the string
|
||||||
|
.Ql %%
|
||||||
|
and all occurences of
|
||||||
|
.Ql %1
|
||||||
|
are replaced by the response to the first prompt, the second
|
||||||
|
.Ql %%
|
||||||
|
and all
|
||||||
|
.Ql %2
|
||||||
|
are replaced with the response to the second prompt, and so on for further
|
||||||
|
prompts. Up to nine prompt responses may be replaced
|
||||||
|
.Ns ( Ql %1
|
||||||
|
to
|
||||||
|
.Ns Ql %9 ) .
|
||||||
.It Xo Ic confirm-before
|
.It Xo Ic confirm-before
|
||||||
.Op Fl t Ar target-client
|
.Op Fl t Ar target-client
|
||||||
.Ar command
|
.Ar command
|
||||||
@ -1926,6 +1949,7 @@ Creating new key bindings:
|
|||||||
.Bd -literal -offset indent
|
.Bd -literal -offset indent
|
||||||
bind-key b set-option status
|
bind-key b set-option status
|
||||||
bind-key / command-prompt "split-window 'exec man %%'"
|
bind-key / command-prompt "split-window 'exec man %%'"
|
||||||
|
bind-key S command-prompt "new-window -n %1 'ssh %1'"
|
||||||
.Ed
|
.Ed
|
||||||
.Sh SEE ALSO
|
.Sh SEE ALSO
|
||||||
.Xr pty 4
|
.Xr pty 4
|
||||||
|
1
tmux.h
1
tmux.h
@ -1440,6 +1440,7 @@ void status_prompt_set(struct client *, const char *,
|
|||||||
void status_prompt_clear(struct client *);
|
void status_prompt_clear(struct client *);
|
||||||
int status_prompt_redraw(struct client *);
|
int status_prompt_redraw(struct client *);
|
||||||
void status_prompt_key(struct client *, int);
|
void status_prompt_key(struct client *, int);
|
||||||
|
void status_prompt_update(struct client *, const char *);
|
||||||
|
|
||||||
/* resize.c */
|
/* resize.c */
|
||||||
void recalculate_sizes(void);
|
void recalculate_sizes(void);
|
||||||
|
Loading…
Reference in New Issue
Block a user