Add different command historys for different types of prompts

("command", "search" etc). From Anindya Mukherjee.
pull/2755/head
nicm 2021-06-10 07:50:03 +00:00
parent 6c659494f5
commit 866117636e
13 changed files with 356 additions and 96 deletions

View File

@ -61,6 +61,7 @@ SRCS= alerts.c \
cmd-show-environment.c \ cmd-show-environment.c \
cmd-show-messages.c \ cmd-show-messages.c \
cmd-show-options.c \ cmd-show-options.c \
cmd-show-prompt-history.c \
cmd-source-file.c \ cmd-source-file.c \
cmd-split-window.c \ cmd-split-window.c \
cmd-swap-pane.c \ cmd-swap-pane.c \

View File

@ -40,25 +40,26 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt", .name = "command-prompt",
.alias = NULL, .alias = NULL,
.args = { "1kiI:Np:Tt:W", 0, 1 }, .args = { "1kiI:Np:t:T:", 0, 1 },
.usage = "[-1kiNTW] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
"[template]", " [-T type] [template]",
.flags = CMD_CLIENT_TFLAG, .flags = CMD_CLIENT_TFLAG,
.exec = cmd_command_prompt_exec .exec = cmd_command_prompt_exec
}; };
struct cmd_command_prompt_cdata { struct cmd_command_prompt_cdata {
int flags; int flags;
enum prompt_type prompt_type;
char *inputs; char *inputs;
char *next_input; char *next_input;
char *prompts; char *prompts;
char *next_prompt; char *next_prompt;
char *template; char *template;
int idx; int idx;
}; };
static enum cmd_retval static enum cmd_retval
@ -67,7 +68,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
struct args *args = cmd_get_args(self); struct args *args = cmd_get_args(self);
struct client *tc = cmdq_get_target_client(item); struct client *tc = cmdq_get_target_client(item);
struct cmd_find_state *target = cmdq_get_target(item); struct cmd_find_state *target = cmdq_get_target(item);
const char *inputs, *prompts; const char *inputs, *prompts, *type;
struct cmd_command_prompt_cdata *cdata; struct cmd_command_prompt_cdata *cdata;
char *prompt, *ptr, *input = NULL; char *prompt, *ptr, *input = NULL;
size_t n; size_t n;
@ -114,6 +115,16 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
input = strsep(&cdata->next_input, ","); input = strsep(&cdata->next_input, ",");
} }
/* Get prompt type. */
if ((type = args_get(args, 'T')) != NULL) {
cdata->prompt_type = status_prompt_type(type);
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "unknown type: %s", type);
return (CMD_RETURN_ERROR);
}
} else
cdata->prompt_type = PROMPT_TYPE_COMMAND;
if (args_has(args, '1')) if (args_has(args, '1'))
cdata->flags |= PROMPT_SINGLE; cdata->flags |= PROMPT_SINGLE;
else if (args_has(args, 'N')) else if (args_has(args, 'N'))
@ -122,13 +133,9 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_INCREMENTAL; cdata->flags |= PROMPT_INCREMENTAL;
else if (args_has(args, 'k')) else if (args_has(args, 'k'))
cdata->flags |= PROMPT_KEY; cdata->flags |= PROMPT_KEY;
else if (args_has(args, 'W'))
cdata->flags |= PROMPT_WINDOW;
else if (args_has(args, 'T'))
cdata->flags |= PROMPT_TARGET;
status_prompt_set(tc, target, prompt, input, status_prompt_set(tc, target, prompt, input,
cmd_command_prompt_callback, cmd_command_prompt_free, cdata, cmd_command_prompt_callback, cmd_command_prompt_free, cdata,
cdata->flags); cdata->flags, cdata->prompt_type);
free(prompt); free(prompt);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

View File

@ -74,7 +74,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item)
status_prompt_set(tc, target, new_prompt, NULL, status_prompt_set(tc, target, new_prompt, NULL,
cmd_confirm_before_callback, cmd_confirm_before_free, cdata, cmd_confirm_before_callback, cmd_confirm_before_free, cdata,
PROMPT_SINGLE); PROMPT_SINGLE, PROMPT_TYPE_COMMAND);
free(new_prompt); free(new_prompt);
return (CMD_RETURN_NORMAL); return (CMD_RETURN_NORMAL);

108
cmd-show-prompt-history.c Normal file
View File

@ -0,0 +1,108 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2021 Anindya Mukherjee <anindya49@hotmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include "tmux.h"
#include <stdlib.h>
/*
* Show or clear prompt history.
*/
static enum cmd_retval cmd_show_prompt_history_exec(struct cmd *,
struct cmdq_item *);
const struct cmd_entry cmd_show_prompt_history_entry = {
.name = "show-prompt-history",
.alias = "showphist",
.args = { "T:", 0, 0 },
.usage = "[-T type]",
.flags = CMD_AFTERHOOK,
.exec = cmd_show_prompt_history_exec
};
const struct cmd_entry cmd_clear_prompt_history_entry = {
.name = "clear-prompt-history",
.alias = "clearphist",
.args = { "T:", 0, 0 },
.usage = "[-T type]",
.flags = CMD_AFTERHOOK,
.exec = cmd_show_prompt_history_exec
};
static enum cmd_retval
cmd_show_prompt_history_exec(struct cmd *self, struct cmdq_item *item)
{
struct args *args = cmd_get_args(self);
const char *typestr = args_get(args, 'T');
enum prompt_type type;
u_int tidx, hidx;
if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) {
if (typestr == NULL) {
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
free(status_prompt_hlist[tidx]);
status_prompt_hlist[tidx] = NULL;
status_prompt_hsize[tidx] = 0;
}
} else {
type = status_prompt_type(typestr);
if (type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR);
}
free(status_prompt_hlist[type]);
status_prompt_hlist[type] = NULL;
status_prompt_hsize[type] = 0;
}
return (CMD_RETURN_NORMAL);
}
if (typestr == NULL) {
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
cmdq_print(item, "History for %s:\n",
status_prompt_type_string(tidx));
for (hidx = 0; hidx < status_prompt_hsize[tidx];
hidx++) {
cmdq_print(item, "%d: %s", hidx + 1,
status_prompt_hlist[tidx][hidx]);
}
cmdq_print(item, "%s", "");
}
} else {
type = status_prompt_type(typestr);
if (type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR);
}
cmdq_print(item, "History for %s:\n",
status_prompt_type_string(type));
for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++) {
cmdq_print(item, "%d: %s", hidx + 1,
status_prompt_hlist[type][hidx]);
}
cmdq_print(item, "%s", "");
}
return (CMD_RETURN_NORMAL);
}

4
cmd.c
View File

@ -36,6 +36,7 @@ extern const struct cmd_entry cmd_choose_buffer_entry;
extern const struct cmd_entry cmd_choose_client_entry; extern const struct cmd_entry cmd_choose_client_entry;
extern const struct cmd_entry cmd_choose_tree_entry; extern const struct cmd_entry cmd_choose_tree_entry;
extern const struct cmd_entry cmd_clear_history_entry; extern const struct cmd_entry cmd_clear_history_entry;
extern const struct cmd_entry cmd_clear_prompt_history_entry;
extern const struct cmd_entry cmd_clock_mode_entry; extern const struct cmd_entry cmd_clock_mode_entry;
extern const struct cmd_entry cmd_command_prompt_entry; extern const struct cmd_entry cmd_command_prompt_entry;
extern const struct cmd_entry cmd_confirm_before_entry; extern const struct cmd_entry cmd_confirm_before_entry;
@ -105,6 +106,7 @@ extern const struct cmd_entry cmd_show_environment_entry;
extern const struct cmd_entry cmd_show_hooks_entry; extern const struct cmd_entry cmd_show_hooks_entry;
extern const struct cmd_entry cmd_show_messages_entry; extern const struct cmd_entry cmd_show_messages_entry;
extern const struct cmd_entry cmd_show_options_entry; extern const struct cmd_entry cmd_show_options_entry;
extern const struct cmd_entry cmd_show_prompt_history_entry;
extern const struct cmd_entry cmd_show_window_options_entry; extern const struct cmd_entry cmd_show_window_options_entry;
extern const struct cmd_entry cmd_source_file_entry; extern const struct cmd_entry cmd_source_file_entry;
extern const struct cmd_entry cmd_split_window_entry; extern const struct cmd_entry cmd_split_window_entry;
@ -127,6 +129,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_choose_client_entry, &cmd_choose_client_entry,
&cmd_choose_tree_entry, &cmd_choose_tree_entry,
&cmd_clear_history_entry, &cmd_clear_history_entry,
&cmd_clear_prompt_history_entry,
&cmd_clock_mode_entry, &cmd_clock_mode_entry,
&cmd_command_prompt_entry, &cmd_command_prompt_entry,
&cmd_confirm_before_entry, &cmd_confirm_before_entry,
@ -195,6 +198,7 @@ const struct cmd_entry *cmd_table[] = {
&cmd_show_hooks_entry, &cmd_show_hooks_entry,
&cmd_show_messages_entry, &cmd_show_messages_entry,
&cmd_show_options_entry, &cmd_show_options_entry,
&cmd_show_prompt_history_entry,
&cmd_show_window_options_entry, &cmd_show_window_options_entry,
&cmd_source_file_entry, &cmd_source_file_entry,
&cmd_split_window_entry, &cmd_split_window_entry,

View File

@ -350,12 +350,12 @@ key_bindings_init(void)
"bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"", "bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"",
"bind -N 'Split window horizontally' % split-window -h", "bind -N 'Split window horizontally' % split-window -h",
"bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window", "bind -N 'Kill current window' & confirm-before -p\"kill-window #W? (y/n)\" kill-window",
"bind -N 'Prompt for window index to select' \"'\" command-prompt -Wpindex \"select-window -t ':%%'\"", "bind -N 'Prompt for window index to select' \"'\" command-prompt -T window-target -pindex \"select-window -t ':%%'\"",
"bind -N 'Switch to previous client' ( switch-client -p", "bind -N 'Switch to previous client' ( switch-client -p",
"bind -N 'Switch to next client' ) switch-client -n", "bind -N 'Switch to next client' ) switch-client -n",
"bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"", "bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"",
"bind -N 'Delete the most recent paste buffer' - delete-buffer", "bind -N 'Delete the most recent paste buffer' - delete-buffer",
"bind -N 'Move the current window' . command-prompt -T \"move-window -t '%%'\"", "bind -N 'Move the current window' . command-prompt -T target \"move-window -t '%%'\"",
"bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'", "bind -N 'Describe key binding' '/' command-prompt -kpkey 'list-keys -1N \"%%%\"'",
"bind -N 'Select window 0' 0 select-window -t:=0", "bind -N 'Select window 0' 0 select-window -t:=0",
"bind -N 'Select window 1' 1 select-window -t:=1", "bind -N 'Select window 1' 1 select-window -t:=1",
@ -479,8 +479,8 @@ key_bindings_init(void)
"bind -Tcopy-mode C-k send -X copy-end-of-line", "bind -Tcopy-mode C-k send -X copy-end-of-line",
"bind -Tcopy-mode C-n send -X cursor-down", "bind -Tcopy-mode C-n send -X cursor-down",
"bind -Tcopy-mode C-p send -X cursor-up", "bind -Tcopy-mode C-p send -X cursor-up",
"bind -Tcopy-mode C-r command-prompt -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'", "bind -Tcopy-mode C-r command-prompt -T search -ip'(search up)' -I'#{pane_search_string}' 'send -X search-backward-incremental \"%%%\"'",
"bind -Tcopy-mode C-s command-prompt -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'", "bind -Tcopy-mode C-s command-prompt -T search -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-incremental \"%%%\"'",
"bind -Tcopy-mode C-v send -X page-down", "bind -Tcopy-mode C-v send -X page-down",
"bind -Tcopy-mode C-w send -X copy-pipe-and-cancel", "bind -Tcopy-mode C-w send -X copy-pipe-and-cancel",
"bind -Tcopy-mode Escape send -X cancel", "bind -Tcopy-mode Escape send -X cancel",
@ -559,7 +559,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi Space send -X begin-selection", "bind -Tcopy-mode-vi Space send -X begin-selection",
"bind -Tcopy-mode-vi '$' send -X end-of-line", "bind -Tcopy-mode-vi '$' send -X end-of-line",
"bind -Tcopy-mode-vi , send -X jump-reverse", "bind -Tcopy-mode-vi , send -X jump-reverse",
"bind -Tcopy-mode-vi / command-prompt -p'(search down)' 'send -X search-forward \"%%%\"'", "bind -Tcopy-mode-vi / command-prompt -T search -p'(search down)' 'send -X search-forward \"%%%\"'",
"bind -Tcopy-mode-vi 0 send -X start-of-line", "bind -Tcopy-mode-vi 0 send -X start-of-line",
"bind -Tcopy-mode-vi 1 command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 1 command-prompt -Np'(repeat)' -I1 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi 2 command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 2 command-prompt -Np'(repeat)' -I2 'send -N \"%%%\"'",
@ -572,7 +572,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi 9 command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"'", "bind -Tcopy-mode-vi 9 command-prompt -Np'(repeat)' -I9 'send -N \"%%%\"'",
"bind -Tcopy-mode-vi : command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", "bind -Tcopy-mode-vi : command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'",
"bind -Tcopy-mode-vi \\; send -X jump-again", "bind -Tcopy-mode-vi \\; send -X jump-again",
"bind -Tcopy-mode-vi ? command-prompt -p'(search up)' 'send -X search-backward \"%%%\"'", "bind -Tcopy-mode-vi ? command-prompt -T search -p'(search up)' 'send -X search-backward \"%%%\"'",
"bind -Tcopy-mode-vi A send -X append-selection-and-cancel", "bind -Tcopy-mode-vi A send -X append-selection-and-cancel",
"bind -Tcopy-mode-vi B send -X previous-space", "bind -Tcopy-mode-vi B send -X previous-space",
"bind -Tcopy-mode-vi D send -X copy-end-of-line", "bind -Tcopy-mode-vi D send -X copy-end-of-line",

View File

@ -1168,7 +1168,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mtd->references++; mtd->references++;
status_prompt_set(c, NULL, "(search) ", "", status_prompt_set(c, NULL, "(search) ", "",
mode_tree_search_callback, mode_tree_search_free, mtd, mode_tree_search_callback, mode_tree_search_free, mtd,
PROMPT_NOFORMAT); PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
break; break;
case 'n': case 'n':
mode_tree_search_set(mtd); mode_tree_search_set(mtd);
@ -1177,7 +1177,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mtd->references++; mtd->references++;
status_prompt_set(c, NULL, "(filter) ", mtd->filter, status_prompt_set(c, NULL, "(filter) ", mtd->filter,
mode_tree_filter_callback, mode_tree_filter_free, mtd, mode_tree_filter_callback, mode_tree_filter_free, mtd,
PROMPT_NOFORMAT); PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
break; break;
case 'v': case 'v':
mtd->preview = !mtd->preview; mtd->preview = !mtd->preview;

View File

@ -302,6 +302,15 @@ const struct options_table_entry options_table[] = {
.text = "Maximum number of server messages to keep." .text = "Maximum number of server messages to keep."
}, },
{ .name = "prompt-history-limit",
.type = OPTIONS_TABLE_NUMBER,
.scope = OPTIONS_TABLE_SERVER,
.minimum = 0,
.maximum = INT_MAX,
.default_num = 100,
.text = "Maximum number of commands to keep in history."
},
{ .name = "set-clipboard", { .name = "set-clipboard",
.type = OPTIONS_TABLE_CHOICE, .type = OPTIONS_TABLE_CHOICE,
.scope = OPTIONS_TABLE_SERVER, .scope = OPTIONS_TABLE_SERVER,

184
status.c
View File

@ -33,9 +33,9 @@ static void status_message_callback(int, short, void *);
static void status_timer_callback(int, short, void *); static void status_timer_callback(int, short, void *);
static char *status_prompt_find_history_file(void); static char *status_prompt_find_history_file(void);
static const char *status_prompt_up_history(u_int *); static const char *status_prompt_up_history(u_int *, u_int);
static const char *status_prompt_down_history(u_int *); static const char *status_prompt_down_history(u_int *, u_int);
static void status_prompt_add_history(const char *); static void status_prompt_add_history(const char *, u_int);
static char *status_prompt_complete(struct client *, const char *, u_int); static char *status_prompt_complete(struct client *, const char *, u_int);
static char *status_prompt_complete_window_menu(struct client *, static char *status_prompt_complete_window_menu(struct client *,
@ -49,10 +49,16 @@ struct status_prompt_menu {
char flag; char flag;
}; };
static const char *prompt_type_strings[] = {
"command",
"search",
"target",
"window-target"
};
/* Status prompt history. */ /* Status prompt history. */
#define PROMPT_HISTORY 100 char **status_prompt_hlist[PROMPT_NTYPES];
static char **status_prompt_hlist; u_int status_prompt_hsize[PROMPT_NTYPES];
static u_int status_prompt_hsize;
/* Find the history file to load/save from/to. */ /* Find the history file to load/save from/to. */
static char * static char *
@ -75,6 +81,28 @@ status_prompt_find_history_file(void)
return (path); return (path);
} }
/* Add loaded history item to the appropriate list. */
static void
status_prompt_add_typed_history(char *line)
{
char *typestr;
enum prompt_type type = PROMPT_TYPE_INVALID;
typestr = strsep(&line, ":");
if (line != NULL)
type = status_prompt_type(typestr);
if (type == PROMPT_TYPE_INVALID) {
/*
* Invalid types are not expected, but this provides backward
* compatibility with old history files.
*/
if (line != NULL)
*(--line) = ':';
status_prompt_add_history(typestr, PROMPT_TYPE_COMMAND);
} else
status_prompt_add_history(line, type);
}
/* Load status prompt history from file. */ /* Load status prompt history from file. */
void void
status_prompt_load_history(void) status_prompt_load_history(void)
@ -102,12 +130,12 @@ status_prompt_load_history(void)
if (length > 0) { if (length > 0) {
if (line[length - 1] == '\n') { if (line[length - 1] == '\n') {
line[length - 1] = '\0'; line[length - 1] = '\0';
status_prompt_add_history(line); status_prompt_add_typed_history(line);
} else { } else {
tmp = xmalloc(length + 1); tmp = xmalloc(length + 1);
memcpy(tmp, line, length); memcpy(tmp, line, length);
tmp[length] = '\0'; tmp[length] = '\0';
status_prompt_add_history(tmp); status_prompt_add_typed_history(tmp);
free(tmp); free(tmp);
} }
} }
@ -120,7 +148,7 @@ void
status_prompt_save_history(void) status_prompt_save_history(void)
{ {
FILE *f; FILE *f;
u_int i; u_int i, type;
char *history_file; char *history_file;
if ((history_file = status_prompt_find_history_file()) == NULL) if ((history_file = status_prompt_find_history_file()) == NULL)
@ -135,9 +163,13 @@ status_prompt_save_history(void)
} }
free(history_file); free(history_file);
for (i = 0; i < status_prompt_hsize; i++) { for (type = 0; type < PROMPT_NTYPES; type++) {
fputs(status_prompt_hlist[i], f); for (i = 0; i < status_prompt_hsize[type]; i++) {
fputc('\n', f); fputs(prompt_type_strings[type], f);
fputc(':', f);
fputs(status_prompt_hlist[type][i], f);
fputc('\n', f);
}
} }
fclose(f); fclose(f);
@ -545,7 +577,7 @@ status_message_redraw(struct client *c)
void void
status_prompt_set(struct client *c, struct cmd_find_state *fs, status_prompt_set(struct client *c, struct cmd_find_state *fs,
const char *msg, const char *input, prompt_input_cb inputcb, const char *msg, const char *input, prompt_input_cb inputcb,
prompt_free_cb freecb, void *data, int flags) prompt_free_cb freecb, void *data, int flags, enum prompt_type prompt_type)
{ {
struct format_tree *ft; struct format_tree *ft;
char *tmp; char *tmp;
@ -581,9 +613,10 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs,
c->prompt_freecb = freecb; c->prompt_freecb = freecb;
c->prompt_data = data; c->prompt_data = data;
c->prompt_hindex = 0; memset(c->prompt_hindex, 0, sizeof c->prompt_hindex);
c->prompt_flags = flags; c->prompt_flags = flags;
c->prompt_type = prompt_type;
c->prompt_mode = PROMPT_ENTRY; c->prompt_mode = PROMPT_ENTRY;
if (~flags & PROMPT_INCREMENTAL) if (~flags & PROMPT_INCREMENTAL)
@ -644,7 +677,7 @@ status_prompt_update(struct client *c, const char *msg, const char *input)
c->prompt_buffer = utf8_fromcstr(tmp); c->prompt_buffer = utf8_fromcstr(tmp);
c->prompt_index = utf8_strlen(c->prompt_buffer); c->prompt_index = utf8_strlen(c->prompt_buffer);
c->prompt_hindex = 0; memset(c->prompt_hindex, 0, sizeof c->prompt_hindex);
c->flags |= CLIENT_REDRAWSTATUS; c->flags |= CLIENT_REDRAWSTATUS;
@ -768,7 +801,7 @@ status_prompt_space(const struct utf8_data *ud)
} }
/* /*
* Translate key from emacs to vi. Return 0 to drop key, 1 to process the key * Translate key from vi to emacs. Return 0 to drop key, 1 to process the key
* as an emacs key; return 2 to append to the buffer. * as an emacs key; return 2 to append to the buffer.
*/ */
static int static int
@ -1222,7 +1255,8 @@ process_key:
goto changed; goto changed;
case KEYC_UP: case KEYC_UP:
case '\020': /* C-p */ case '\020': /* C-p */
histstr = status_prompt_up_history(&c->prompt_hindex); histstr = status_prompt_up_history(c->prompt_hindex,
c->prompt_type);
if (histstr == NULL) if (histstr == NULL)
break; break;
free(c->prompt_buffer); free(c->prompt_buffer);
@ -1231,7 +1265,8 @@ process_key:
goto changed; goto changed;
case KEYC_DOWN: case KEYC_DOWN:
case '\016': /* C-n */ case '\016': /* C-n */
histstr = status_prompt_down_history(&c->prompt_hindex); histstr = status_prompt_down_history(c->prompt_hindex,
c->prompt_type);
if (histstr == NULL) if (histstr == NULL)
break; break;
free(c->prompt_buffer); free(c->prompt_buffer);
@ -1259,7 +1294,7 @@ process_key:
case '\n': case '\n':
s = utf8_tocstr(c->prompt_buffer); s = utf8_tocstr(c->prompt_buffer);
if (*s != '\0') if (*s != '\0')
status_prompt_add_history(s); status_prompt_add_history(s, c->prompt_type);
if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0) if (c->prompt_inputcb(c, c->prompt_data, s, 1) == 0)
status_prompt_clear(c); status_prompt_clear(c);
free(s); free(s);
@ -1348,54 +1383,78 @@ changed:
/* Get previous line from the history. */ /* Get previous line from the history. */
static const char * static const char *
status_prompt_up_history(u_int *idx) status_prompt_up_history(u_int *idx, u_int type)
{ {
/* /*
* History runs from 0 to size - 1. Index is from 0 to size. Zero is * History runs from 0 to size - 1. Index is from 0 to size. Zero is
* empty. * empty.
*/ */
if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) if (status_prompt_hsize[type] == 0 ||
idx[type] == status_prompt_hsize[type])
return (NULL); return (NULL);
(*idx)++; idx[type]++;
return (status_prompt_hlist[status_prompt_hsize - *idx]); return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]);
} }
/* Get next line from the history. */ /* Get next line from the history. */
static const char * static const char *
status_prompt_down_history(u_int *idx) status_prompt_down_history(u_int *idx, u_int type)
{ {
if (status_prompt_hsize == 0 || *idx == 0) if (status_prompt_hsize[type] == 0 || idx[type] == 0)
return (""); return ("");
(*idx)--; idx[type]--;
if (*idx == 0) if (idx[type] == 0)
return (""); return ("");
return (status_prompt_hlist[status_prompt_hsize - *idx]); return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]);
} }
/* Add line to the history. */ /* Add line to the history. */
static void static void
status_prompt_add_history(const char *line) status_prompt_add_history(const char *line, u_int type)
{ {
size_t size; u_int i, oldsize, newsize, freecount, hlimit, new = 1;
size_t movesize;
if (status_prompt_hsize > 0 && oldsize = status_prompt_hsize[type];
strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) if (oldsize > 0 &&
return; strcmp(status_prompt_hlist[type][oldsize - 1], line) == 0)
new = 0;
if (status_prompt_hsize == PROMPT_HISTORY) { hlimit = options_get_number(global_options, "prompt-history-limit");
free(status_prompt_hlist[0]); if (hlimit > oldsize) {
if (new == 0)
size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; return;
memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); newsize = oldsize + new;
} else {
status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); newsize = hlimit;
return; freecount = oldsize + new - newsize;
if (freecount > oldsize)
freecount = oldsize;
if (freecount == 0)
return;
for (i = 0; i < freecount; i++)
free(status_prompt_hlist[type][i]);
movesize = (oldsize - freecount) *
sizeof *status_prompt_hlist[type];
if (movesize > 0) {
memmove(&status_prompt_hlist[type][0],
&status_prompt_hlist[type][freecount], movesize);
}
} }
status_prompt_hlist = xreallocarray(status_prompt_hlist, if (newsize == 0) {
status_prompt_hsize + 1, sizeof *status_prompt_hlist); free(status_prompt_hlist[type]);
status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); status_prompt_hlist[type] = NULL;
} else if (newsize != oldsize) {
status_prompt_hlist[type] =
xreallocarray(status_prompt_hlist[type], newsize,
sizeof *status_prompt_hlist[type]);
}
if (new == 1 && newsize > 0)
status_prompt_hlist[type][newsize - 1] = xstrdup(line);
status_prompt_hsize[type] = newsize;
} }
/* Build completion list. */ /* Build completion list. */
@ -1501,7 +1560,7 @@ status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key,
s = xstrdup(spm->list[idx]); s = xstrdup(spm->list[idx]);
else else
xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]); xasprintf(&s, "-%c%s", spm->flag, spm->list[idx]);
if (c->prompt_flags & PROMPT_WINDOW) { if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) {
free(c->prompt_buffer); free(c->prompt_buffer);
c->prompt_buffer = utf8_fromcstr(s); c->prompt_buffer = utf8_fromcstr(s);
c->prompt_index = utf8_strlen(c->prompt_buffer); c->prompt_index = utf8_strlen(c->prompt_buffer);
@ -1609,7 +1668,7 @@ status_prompt_complete_window_menu(struct client *c, struct session *s,
} }
list = xreallocarray(list, size + 1, sizeof *list); list = xreallocarray(list, size + 1, sizeof *list);
if (c->prompt_flags & PROMPT_WINDOW) { if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) {
xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name);
xasprintf(&list[size++], "%d", wl->idx); xasprintf(&list[size++], "%d", wl->idx);
} else { } else {
@ -1717,10 +1776,12 @@ status_prompt_complete(struct client *c, const char *word, u_int offset)
u_int size = 0, i; u_int size = 0, i;
if (*word == '\0' && if (*word == '\0' &&
((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0)) c->prompt_type != PROMPT_TYPE_TARGET &&
c->prompt_type != PROMPT_TYPE_WINDOW_TARGET)
return (NULL); return (NULL);
if (((c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) == 0) && if (c->prompt_type != PROMPT_TYPE_TARGET &&
c->prompt_type != PROMPT_TYPE_WINDOW_TARGET &&
strncmp(word, "-t", 2) != 0 && strncmp(word, "-t", 2) != 0 &&
strncmp(word, "-s", 2) != 0) { strncmp(word, "-s", 2) != 0) {
list = status_prompt_complete_list(&size, word, offset == 0); list = status_prompt_complete_list(&size, word, offset == 0);
@ -1733,7 +1794,8 @@ status_prompt_complete(struct client *c, const char *word, u_int offset)
goto found; goto found;
} }
if (c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) { if (c->prompt_type == PROMPT_TYPE_TARGET ||
c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) {
s = word; s = word;
flag = '\0'; flag = '\0';
} else { } else {
@ -1743,7 +1805,7 @@ status_prompt_complete(struct client *c, const char *word, u_int offset)
} }
/* If this is a window completion, open the window menu. */ /* If this is a window completion, open the window menu. */
if (c->prompt_flags & PROMPT_WINDOW) { if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) {
out = status_prompt_complete_window_menu(c, c->session, s, out = status_prompt_complete_window_menu(c, c->session, s,
offset, '\0'); offset, '\0');
goto found; goto found;
@ -1793,3 +1855,25 @@ found:
} }
return (out); return (out);
} }
/* Return the type of the prompt as an enum. */
enum prompt_type
status_prompt_type(const char *type)
{
u_int i;
for (i = 0; i < PROMPT_NTYPES; i++) {
if (strcmp(type, status_prompt_type_string(i)) == 0)
return (i);
}
return (PROMPT_TYPE_INVALID);
}
/* Accessor for prompt_type_strings. */
const char *
status_prompt_type_string(u_int type)
{
if (type >= PROMPT_NTYPES)
return ("invalid");
return (prompt_type_strings[type]);
}

47
tmux.1
View File

@ -3453,7 +3453,9 @@ will write command prompt history on exit and load it from on start.
.It Ic message-limit Ar number .It Ic message-limit Ar number
Set the number of error or information messages to save in the message log for Set the number of error or information messages to save in the message log for
each client. each client.
The default is 100. .It Ic prompt-history-limit Ar number
Set the number of history items to save in the history file for each type of
command prompt.
.It Xo Ic set-clipboard .It Xo Ic set-clipboard
.Op Ic on | external | off .Op Ic on | external | off
.Xc .Xc
@ -5376,11 +5378,25 @@ session option.
.Pp .Pp
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 clear-prompt-history
.Op Fl T Ar prompt-type
.Xc
.D1 (alias: Ic clrphist)
Clear status prompt history for prompt type
.Ar prompt-type .
If
.Fl T
is omitted, then clear history for all types.
See
.Ic command-prompt
for possible values for
.Ar prompt-type .
.It Xo Ic command-prompt .It Xo Ic command-prompt
.Op Fl 1ikNTW .Op Fl 1ikN
.Op Fl I Ar inputs .Op Fl I Ar inputs
.Op Fl p Ar prompts .Op Fl p Ar prompts
.Op Fl t Ar target-client .Op Fl t Ar target-client
.Op Fl T Ar prompt-type
.Op Ar template .Op Ar template
.Xc .Xc
Open the command prompt in a client. Open the command prompt in a client.
@ -5436,14 +5452,20 @@ makes the prompt only accept numeric key presses.
.Fl i .Fl i
executes the command every time the prompt input changes instead of when the executes the command every time the prompt input changes instead of when the
user exits the command prompt. user exits the command prompt.
.Pp
.Fl T .Fl T
tells tells
.Nm .Nm
that the prompt is for a target which affects what completions are offered when the prompt type.
This affects what completions are offered when
.Em Tab .Em Tab
is pressed; is pressed.
.Fl W Available types are:
is similar but indicates the prompt is for a window. .Ql command ,
.Ql search ,
.Ql target
and
.Ql window-target .
.Pp .Pp
The following keys have a special meaning in the command prompt, depending The following keys have a special meaning in the command prompt, depending
on the value of the on the value of the
@ -5665,6 +5687,19 @@ If omitted, half of the terminal size is used.
The The
.Fl C .Fl C
flag closes any popup on the client. flag closes any popup on the client.
.It Xo Ic show-prompt-history
.Op Fl T Ar prompt-type
.Xc
.D1 (alias: Ic showphist)
Display status prompt history for prompt type
.Ar prompt-type .
If
.Fl T
is omitted, then show history for all types.
See
.Ic command-prompt
for possible values for
.Ar prompt-type .
.El .El
.Sh BUFFERS .Sh BUFFERS
.Nm .Nm

22
tmux.h
View File

@ -1571,6 +1571,16 @@ struct status_line {
struct status_line_entry entries[STATUS_LINES_LIMIT]; struct status_line_entry entries[STATUS_LINES_LIMIT];
}; };
/* Prompt type. */
#define PROMPT_NTYPES 4
enum prompt_type {
PROMPT_TYPE_COMMAND,
PROMPT_TYPE_SEARCH,
PROMPT_TYPE_TARGET,
PROMPT_TYPE_WINDOW_TARGET,
PROMPT_TYPE_INVALID = 0xff
};
/* File in client. */ /* File in client. */
typedef void (*client_file_cb) (struct client *, const char *, int, int, typedef void (*client_file_cb) (struct client *, const char *, int, int,
struct evbuffer *, void *); struct evbuffer *, void *);
@ -1734,18 +1744,16 @@ struct client {
prompt_input_cb prompt_inputcb; prompt_input_cb prompt_inputcb;
prompt_free_cb prompt_freecb; prompt_free_cb prompt_freecb;
void *prompt_data; void *prompt_data;
u_int prompt_hindex; u_int prompt_hindex[PROMPT_NTYPES];
enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode;
struct utf8_data *prompt_saved; struct utf8_data *prompt_saved;
#define PROMPT_SINGLE 0x1 #define PROMPT_SINGLE 0x1
#define PROMPT_NUMERIC 0x2 #define PROMPT_NUMERIC 0x2
#define PROMPT_INCREMENTAL 0x4 #define PROMPT_INCREMENTAL 0x4
#define PROMPT_NOFORMAT 0x8 #define PROMPT_NOFORMAT 0x8
#define PROMPT_KEY 0x10 #define PROMPT_KEY 0x10
#define PROMPT_WINDOW 0x20
#define PROMPT_TARGET 0x40
int prompt_flags; int prompt_flags;
enum prompt_type prompt_type;
struct session *session; struct session *session;
struct session *last_session; struct session *last_session;
@ -2517,6 +2525,8 @@ void server_check_unattached(void);
void server_unzoom_window(struct window *); void server_unzoom_window(struct window *);
/* status.c */ /* status.c */
extern char **status_prompt_hlist[];
extern u_int status_prompt_hsize[];
void status_timer_start(struct client *); void status_timer_start(struct client *);
void status_timer_start_all(void); void status_timer_start_all(void);
void status_update_cache(struct session *); void status_update_cache(struct session *);
@ -2531,13 +2541,15 @@ void status_message_clear(struct client *);
int status_message_redraw(struct client *); int status_message_redraw(struct client *);
void status_prompt_set(struct client *, struct cmd_find_state *, void status_prompt_set(struct client *, struct cmd_find_state *,
const char *, const char *, prompt_input_cb, prompt_free_cb, const char *, const char *, prompt_input_cb, prompt_free_cb,
void *, int); void *, int, enum prompt_type);
void status_prompt_clear(struct client *); void status_prompt_clear(struct client *);
int status_prompt_redraw(struct client *); int status_prompt_redraw(struct client *);
int status_prompt_key(struct client *, key_code); int status_prompt_key(struct client *, key_code);
void status_prompt_update(struct client *, const char *, const char *); void status_prompt_update(struct client *, const char *, const char *);
void status_prompt_load_history(void); void status_prompt_load_history(void);
void status_prompt_save_history(void); void status_prompt_save_history(void);
const char *status_prompt_type_string(u_int);
enum prompt_type status_prompt_type(const char *type);
/* resize.c */ /* resize.c */
void resize_window(struct window *, u_int, u_int, int, int); void resize_window(struct window *, u_int, u_int, int, int);

View File

@ -1123,7 +1123,7 @@ window_customize_set_option(struct client *c,
status_prompt_set(c, NULL, prompt, value, status_prompt_set(c, NULL, prompt, value,
window_customize_set_option_callback, window_customize_set_option_callback,
window_customize_free_item_callback, new_item, window_customize_free_item_callback, new_item,
PROMPT_NOFORMAT); PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
free(prompt); free(prompt);
free(value); free(value);
@ -1264,7 +1264,7 @@ window_customize_set_key(struct client *c,
status_prompt_set(c, NULL, prompt, value, status_prompt_set(c, NULL, prompt, value,
window_customize_set_command_callback, window_customize_set_command_callback,
window_customize_free_item_callback, new_item, window_customize_free_item_callback, new_item,
PROMPT_NOFORMAT); PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
free(prompt); free(prompt);
free(value); free(value);
} else if (strcmp(s, "Note") == 0) { } else if (strcmp(s, "Note") == 0) {
@ -1281,7 +1281,7 @@ window_customize_set_key(struct client *c,
(bd->note == NULL ? "" : bd->note), (bd->note == NULL ? "" : bd->note),
window_customize_set_note_callback, window_customize_set_note_callback,
window_customize_free_item_callback, new_item, window_customize_free_item_callback, new_item,
PROMPT_NOFORMAT); PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
free(prompt); free(prompt);
} }
} }
@ -1458,7 +1458,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
status_prompt_set(c, NULL, prompt, "", status_prompt_set(c, NULL, prompt, "",
window_customize_change_current_callback, window_customize_change_current_callback,
window_customize_free_callback, data, window_customize_free_callback, data,
PROMPT_SINGLE|PROMPT_NOFORMAT); PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
free(prompt); free(prompt);
break; break;
case 'D': case 'D':
@ -1471,7 +1471,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
status_prompt_set(c, NULL, prompt, "", status_prompt_set(c, NULL, prompt, "",
window_customize_change_tagged_callback, window_customize_change_tagged_callback,
window_customize_free_callback, data, window_customize_free_callback, data,
PROMPT_SINGLE|PROMPT_NOFORMAT); PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
free(prompt); free(prompt);
break; break;
case 'u': case 'u':
@ -1487,7 +1487,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
status_prompt_set(c, NULL, prompt, "", status_prompt_set(c, NULL, prompt, "",
window_customize_change_current_callback, window_customize_change_current_callback,
window_customize_free_callback, data, window_customize_free_callback, data,
PROMPT_SINGLE|PROMPT_NOFORMAT); PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
free(prompt); free(prompt);
break; break;
case 'U': case 'U':
@ -1500,7 +1500,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
status_prompt_set(c, NULL, prompt, "", status_prompt_set(c, NULL, prompt, "",
window_customize_change_tagged_callback, window_customize_change_tagged_callback,
window_customize_free_callback, data, window_customize_free_callback, data,
PROMPT_SINGLE|PROMPT_NOFORMAT); PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
free(prompt); free(prompt);
break; break;
case 'H': case 'H':

View File

@ -1296,7 +1296,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c,
data->references++; data->references++;
status_prompt_set(c, NULL, prompt, "", status_prompt_set(c, NULL, prompt, "",
window_tree_kill_current_callback, window_tree_command_free, window_tree_kill_current_callback, window_tree_command_free,
data, PROMPT_SINGLE|PROMPT_NOFORMAT); data, PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
free(prompt); free(prompt);
break; break;
case 'X': case 'X':
@ -1307,7 +1307,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c,
data->references++; data->references++;
status_prompt_set(c, NULL, prompt, "", status_prompt_set(c, NULL, prompt, "",
window_tree_kill_tagged_callback, window_tree_command_free, window_tree_kill_tagged_callback, window_tree_command_free,
data, PROMPT_SINGLE|PROMPT_NOFORMAT); data, PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
free(prompt); free(prompt);
break; break;
case ':': case ':':
@ -1319,7 +1319,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c,
data->references++; data->references++;
status_prompt_set(c, NULL, prompt, "", status_prompt_set(c, NULL, prompt, "",
window_tree_command_callback, window_tree_command_free, window_tree_command_callback, window_tree_command_free,
data, PROMPT_NOFORMAT); data, PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
free(prompt); free(prompt);
break; break;
case '\r': case '\r':