1
0
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:
Nicholas Marriott 2009-08-19 10:39:50 +00:00
parent 036de0c5e4
commit 3f4418d84d
4 changed files with 238 additions and 77 deletions

View File

@ -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);
}

View File

@ -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
View File

@ -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
View File

@ -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);