From 866117636e47c9a5d961df2ac40456b7c910a932 Mon Sep 17 00:00:00 2001 From: nicm Date: Thu, 10 Jun 2021 07:50:03 +0000 Subject: [PATCH] Add different command historys for different types of prompts ("command", "search" etc). From Anindya Mukherjee. --- Makefile | 1 + cmd-command-prompt.c | 39 ++++---- cmd-confirm-before.c | 2 +- cmd-show-prompt-history.c | 108 ++++++++++++++++++++++ cmd.c | 4 + key-bindings.c | 12 +-- mode-tree.c | 4 +- options-table.c | 9 ++ status.c | 184 +++++++++++++++++++++++++++----------- tmux.1 | 47 ++++++++-- tmux.h | 22 +++-- window-customize.c | 14 +-- window-tree.c | 6 +- 13 files changed, 356 insertions(+), 96 deletions(-) create mode 100644 cmd-show-prompt-history.c diff --git a/Makefile b/Makefile index 21141317..4cbbd34a 100644 --- a/Makefile +++ b/Makefile @@ -61,6 +61,7 @@ SRCS= alerts.c \ cmd-show-environment.c \ cmd-show-messages.c \ cmd-show-options.c \ + cmd-show-prompt-history.c \ cmd-source-file.c \ cmd-split-window.c \ cmd-swap-pane.c \ diff --git a/cmd-command-prompt.c b/cmd-command-prompt.c index c82235c5..c2a2dec7 100644 --- a/cmd-command-prompt.c +++ b/cmd-command-prompt.c @@ -40,25 +40,26 @@ const struct cmd_entry cmd_command_prompt_entry = { .name = "command-prompt", .alias = NULL, - .args = { "1kiI:Np:Tt:W", 0, 1 }, - .usage = "[-1kiNTW] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE " " - "[template]", + .args = { "1kiI:Np:t:T:", 0, 1 }, + .usage = "[-1kiN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE + " [-T type] [template]", .flags = CMD_CLIENT_TFLAG, .exec = cmd_command_prompt_exec }; struct cmd_command_prompt_cdata { - int flags; + int flags; + enum prompt_type prompt_type; - char *inputs; - char *next_input; + char *inputs; + char *next_input; - char *prompts; - char *next_prompt; + char *prompts; + char *next_prompt; - char *template; - int idx; + char *template; + int idx; }; 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 client *tc = cmdq_get_target_client(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; char *prompt, *ptr, *input = NULL; size_t n; @@ -114,6 +115,16 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item) 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')) cdata->flags |= PROMPT_SINGLE; 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; else if (args_has(args, 'k')) 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, cmd_command_prompt_callback, cmd_command_prompt_free, cdata, - cdata->flags); + cdata->flags, cdata->prompt_type); free(prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-confirm-before.c b/cmd-confirm-before.c index 0c562836..0b490b17 100644 --- a/cmd-confirm-before.c +++ b/cmd-confirm-before.c @@ -74,7 +74,7 @@ cmd_confirm_before_exec(struct cmd *self, struct cmdq_item *item) status_prompt_set(tc, target, new_prompt, NULL, cmd_confirm_before_callback, cmd_confirm_before_free, cdata, - PROMPT_SINGLE); + PROMPT_SINGLE, PROMPT_TYPE_COMMAND); free(new_prompt); return (CMD_RETURN_NORMAL); diff --git a/cmd-show-prompt-history.c b/cmd-show-prompt-history.c new file mode 100644 index 00000000..2091ac9d --- /dev/null +++ b/cmd-show-prompt-history.c @@ -0,0 +1,108 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2021 Anindya Mukherjee + * + * 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 + +/* + * 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); +} diff --git a/cmd.c b/cmd.c index 6b443fa2..90fa221a 100644 --- a/cmd.c +++ b/cmd.c @@ -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_tree_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_command_prompt_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_messages_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_source_file_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_tree_entry, &cmd_clear_history_entry, + &cmd_clear_prompt_history_entry, &cmd_clock_mode_entry, &cmd_command_prompt_entry, &cmd_confirm_before_entry, @@ -195,6 +198,7 @@ const struct cmd_entry *cmd_table[] = { &cmd_show_hooks_entry, &cmd_show_messages_entry, &cmd_show_options_entry, + &cmd_show_prompt_history_entry, &cmd_show_window_options_entry, &cmd_source_file_entry, &cmd_split_window_entry, diff --git a/key-bindings.c b/key-bindings.c index 467c7f93..b380f2cd 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -350,12 +350,12 @@ key_bindings_init(void) "bind -N 'Rename current session' '$' command-prompt -I'#S' \"rename-session -- '%%'\"", "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 '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 next client' ) switch-client -n", "bind -N 'Rename current window' , command-prompt -I'#W' \"rename-window -- '%%'\"", "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 'Select window 0' 0 select-window -t:=0", "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-n send -X cursor-down", "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-s command-prompt -ip'(search down)' -I'#{pane_search_string}' 'send -X search-forward-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 -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-w send -X copy-pipe-and-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 '$' send -X end-of-line", "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 1 command-prompt -Np'(repeat)' -I1 '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 : command-prompt -p'(goto line)' 'send -X goto-line \"%%%\"'", "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 B send -X previous-space", "bind -Tcopy-mode-vi D send -X copy-end-of-line", diff --git a/mode-tree.c b/mode-tree.c index ca7f33a4..807c1dcb 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -1168,7 +1168,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, mtd->references++; status_prompt_set(c, NULL, "(search) ", "", mode_tree_search_callback, mode_tree_search_free, mtd, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH); break; case 'n': 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++; status_prompt_set(c, NULL, "(filter) ", mtd->filter, mode_tree_filter_callback, mode_tree_filter_free, mtd, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH); break; case 'v': mtd->preview = !mtd->preview; diff --git a/options-table.c b/options-table.c index 4f4d9ff6..73c7fd63 100644 --- a/options-table.c +++ b/options-table.c @@ -302,6 +302,15 @@ const struct options_table_entry options_table[] = { .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", .type = OPTIONS_TABLE_CHOICE, .scope = OPTIONS_TABLE_SERVER, diff --git a/status.c b/status.c index f4418500..7bcd2f38 100644 --- a/status.c +++ b/status.c @@ -33,9 +33,9 @@ static void status_message_callback(int, short, void *); static void status_timer_callback(int, short, void *); static char *status_prompt_find_history_file(void); -static const char *status_prompt_up_history(u_int *); -static const char *status_prompt_down_history(u_int *); -static void status_prompt_add_history(const char *); +static const char *status_prompt_up_history(u_int *, u_int); +static const char *status_prompt_down_history(u_int *, u_int); +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_window_menu(struct client *, @@ -49,10 +49,16 @@ struct status_prompt_menu { char flag; }; +static const char *prompt_type_strings[] = { + "command", + "search", + "target", + "window-target" +}; + /* Status prompt history. */ -#define PROMPT_HISTORY 100 -static char **status_prompt_hlist; -static u_int status_prompt_hsize; +char **status_prompt_hlist[PROMPT_NTYPES]; +u_int status_prompt_hsize[PROMPT_NTYPES]; /* Find the history file to load/save from/to. */ static char * @@ -75,6 +81,28 @@ status_prompt_find_history_file(void) 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. */ void status_prompt_load_history(void) @@ -102,12 +130,12 @@ status_prompt_load_history(void) if (length > 0) { if (line[length - 1] == '\n') { line[length - 1] = '\0'; - status_prompt_add_history(line); + status_prompt_add_typed_history(line); } else { tmp = xmalloc(length + 1); memcpy(tmp, line, length); tmp[length] = '\0'; - status_prompt_add_history(tmp); + status_prompt_add_typed_history(tmp); free(tmp); } } @@ -120,7 +148,7 @@ void status_prompt_save_history(void) { FILE *f; - u_int i; + u_int i, type; char *history_file; if ((history_file = status_prompt_find_history_file()) == NULL) @@ -135,9 +163,13 @@ status_prompt_save_history(void) } free(history_file); - for (i = 0; i < status_prompt_hsize; i++) { - fputs(status_prompt_hlist[i], f); - fputc('\n', f); + for (type = 0; type < PROMPT_NTYPES; type++) { + for (i = 0; i < status_prompt_hsize[type]; i++) { + fputs(prompt_type_strings[type], f); + fputc(':', f); + fputs(status_prompt_hlist[type][i], f); + fputc('\n', f); + } } fclose(f); @@ -545,7 +577,7 @@ status_message_redraw(struct client *c) void status_prompt_set(struct client *c, struct cmd_find_state *fs, 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; char *tmp; @@ -581,9 +613,10 @@ status_prompt_set(struct client *c, struct cmd_find_state *fs, c->prompt_freecb = freecb; c->prompt_data = data; - c->prompt_hindex = 0; + memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); c->prompt_flags = flags; + c->prompt_type = prompt_type; c->prompt_mode = PROMPT_ENTRY; 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_index = utf8_strlen(c->prompt_buffer); - c->prompt_hindex = 0; + memset(c->prompt_hindex, 0, sizeof c->prompt_hindex); 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. */ static int @@ -1222,7 +1255,8 @@ process_key: goto changed; case KEYC_UP: 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) break; free(c->prompt_buffer); @@ -1231,7 +1265,8 @@ process_key: goto changed; case KEYC_DOWN: 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) break; free(c->prompt_buffer); @@ -1259,7 +1294,7 @@ process_key: case '\n': s = utf8_tocstr(c->prompt_buffer); 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) status_prompt_clear(c); free(s); @@ -1348,54 +1383,78 @@ changed: /* Get previous line from the history. */ 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 * empty. */ - if (status_prompt_hsize == 0 || *idx == status_prompt_hsize) + if (status_prompt_hsize[type] == 0 || + idx[type] == status_prompt_hsize[type]) return (NULL); - (*idx)++; - return (status_prompt_hlist[status_prompt_hsize - *idx]); + idx[type]++; + return (status_prompt_hlist[type][status_prompt_hsize[type] - idx[type]]); } /* Get next line from the history. */ 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 (""); - (*idx)--; - if (*idx == 0) + idx[type]--; + if (idx[type] == 0) 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. */ 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 && - strcmp(status_prompt_hlist[status_prompt_hsize - 1], line) == 0) - return; + oldsize = status_prompt_hsize[type]; + if (oldsize > 0 && + strcmp(status_prompt_hlist[type][oldsize - 1], line) == 0) + new = 0; - if (status_prompt_hsize == PROMPT_HISTORY) { - free(status_prompt_hlist[0]); - - size = (PROMPT_HISTORY - 1) * sizeof *status_prompt_hlist; - memmove(&status_prompt_hlist[0], &status_prompt_hlist[1], size); - - status_prompt_hlist[status_prompt_hsize - 1] = xstrdup(line); - return; + hlimit = options_get_number(global_options, "prompt-history-limit"); + if (hlimit > oldsize) { + if (new == 0) + return; + newsize = oldsize + new; + } else { + newsize = hlimit; + 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, - status_prompt_hsize + 1, sizeof *status_prompt_hlist); - status_prompt_hlist[status_prompt_hsize++] = xstrdup(line); + if (newsize == 0) { + free(status_prompt_hlist[type]); + 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. */ @@ -1501,7 +1560,7 @@ status_prompt_menu_callback(__unused struct menu *menu, u_int idx, key_code key, s = xstrdup(spm->list[idx]); else 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); c->prompt_buffer = utf8_fromcstr(s); 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); - if (c->prompt_flags & PROMPT_WINDOW) { + if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { xasprintf(&tmp, "%d (%s)", wl->idx, wl->window->name); xasprintf(&list[size++], "%d", wl->idx); } else { @@ -1717,10 +1776,12 @@ status_prompt_complete(struct client *c, const char *word, u_int offset) u_int size = 0, i; 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); - 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, "-s", 2) != 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; } - if (c->prompt_flags & (PROMPT_TARGET|PROMPT_WINDOW)) { + if (c->prompt_type == PROMPT_TYPE_TARGET || + c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { s = word; flag = '\0'; } 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 (c->prompt_flags & PROMPT_WINDOW) { + if (c->prompt_type == PROMPT_TYPE_WINDOW_TARGET) { out = status_prompt_complete_window_menu(c, c->session, s, offset, '\0'); goto found; @@ -1793,3 +1855,25 @@ found: } 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]); +} diff --git a/tmux.1 b/tmux.1 index 47cf4f2c..50b3b6b7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3453,7 +3453,9 @@ will write command prompt history on exit and load it from on start. .It Ic message-limit Ar number Set the number of error or information messages to save in the message log for 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 .Op Ic on | external | off .Xc @@ -5376,11 +5378,25 @@ session option. .Pp Commands related to the status line are as follows: .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 -.Op Fl 1ikNTW +.Op Fl 1ikN .Op Fl I Ar inputs .Op Fl p Ar prompts .Op Fl t Ar target-client +.Op Fl T Ar prompt-type .Op Ar template .Xc Open the command prompt in a client. @@ -5436,14 +5452,20 @@ makes the prompt only accept numeric key presses. .Fl i executes the command every time the prompt input changes instead of when the user exits the command prompt. +.Pp .Fl T tells .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 -is pressed; -.Fl W -is similar but indicates the prompt is for a window. +is pressed. +Available types are: +.Ql command , +.Ql search , +.Ql target +and +.Ql window-target . .Pp The following keys have a special meaning in the command prompt, depending on the value of the @@ -5665,6 +5687,19 @@ If omitted, half of the terminal size is used. The .Fl C 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 .Sh BUFFERS .Nm diff --git a/tmux.h b/tmux.h index fbe19136..5fefcef5 100644 --- a/tmux.h +++ b/tmux.h @@ -1571,6 +1571,16 @@ struct status_line { 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. */ typedef void (*client_file_cb) (struct client *, const char *, int, int, struct evbuffer *, void *); @@ -1734,18 +1744,16 @@ struct client { prompt_input_cb prompt_inputcb; prompt_free_cb prompt_freecb; void *prompt_data; - u_int prompt_hindex; + u_int prompt_hindex[PROMPT_NTYPES]; enum { PROMPT_ENTRY, PROMPT_COMMAND } prompt_mode; struct utf8_data *prompt_saved; - #define PROMPT_SINGLE 0x1 #define PROMPT_NUMERIC 0x2 #define PROMPT_INCREMENTAL 0x4 #define PROMPT_NOFORMAT 0x8 #define PROMPT_KEY 0x10 -#define PROMPT_WINDOW 0x20 -#define PROMPT_TARGET 0x40 int prompt_flags; + enum prompt_type prompt_type; struct session *session; struct session *last_session; @@ -2517,6 +2525,8 @@ void server_check_unattached(void); void server_unzoom_window(struct window *); /* status.c */ +extern char **status_prompt_hlist[]; +extern u_int status_prompt_hsize[]; void status_timer_start(struct client *); void status_timer_start_all(void); void status_update_cache(struct session *); @@ -2531,13 +2541,15 @@ void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, struct cmd_find_state *, const char *, const char *, prompt_input_cb, prompt_free_cb, - void *, int); + void *, int, enum prompt_type); void status_prompt_clear(struct client *); int status_prompt_redraw(struct client *); int status_prompt_key(struct client *, key_code); void status_prompt_update(struct client *, const char *, const char *); void status_prompt_load_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 */ void resize_window(struct window *, u_int, u_int, int, int); diff --git a/window-customize.c b/window-customize.c index 34a13f73..782542f7 100644 --- a/window-customize.c +++ b/window-customize.c @@ -1123,7 +1123,7 @@ window_customize_set_option(struct client *c, status_prompt_set(c, NULL, prompt, value, window_customize_set_option_callback, window_customize_free_item_callback, new_item, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); free(value); @@ -1264,7 +1264,7 @@ window_customize_set_key(struct client *c, status_prompt_set(c, NULL, prompt, value, window_customize_set_command_callback, window_customize_free_item_callback, new_item, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); free(value); } else if (strcmp(s, "Note") == 0) { @@ -1281,7 +1281,7 @@ window_customize_set_key(struct client *c, (bd->note == NULL ? "" : bd->note), window_customize_set_note_callback, window_customize_free_item_callback, new_item, - PROMPT_NOFORMAT); + PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); } } @@ -1458,7 +1458,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_current_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'D': @@ -1471,7 +1471,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_tagged_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'u': @@ -1487,7 +1487,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_current_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'U': @@ -1500,7 +1500,7 @@ window_customize_key(struct window_mode_entry *wme, struct client *c, status_prompt_set(c, NULL, prompt, "", window_customize_change_tagged_callback, window_customize_free_callback, data, - PROMPT_SINGLE|PROMPT_NOFORMAT); + PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'H': diff --git a/window-tree.c b/window-tree.c index 881896e7..f8e38399 100644 --- a/window-tree.c +++ b/window-tree.c @@ -1296,7 +1296,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, data->references++; status_prompt_set(c, NULL, prompt, "", window_tree_kill_current_callback, window_tree_command_free, - data, PROMPT_SINGLE|PROMPT_NOFORMAT); + data, PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case 'X': @@ -1307,7 +1307,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, data->references++; status_prompt_set(c, NULL, prompt, "", window_tree_kill_tagged_callback, window_tree_command_free, - data, PROMPT_SINGLE|PROMPT_NOFORMAT); + data, PROMPT_SINGLE|PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case ':': @@ -1319,7 +1319,7 @@ window_tree_key(struct window_mode_entry *wme, struct client *c, data->references++; status_prompt_set(c, NULL, prompt, "", window_tree_command_callback, window_tree_command_free, - data, PROMPT_NOFORMAT); + data, PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND); free(prompt); break; case '\r':