Major rework of prompts. The basic prompt mechanics (draw, editing, etc)

are now wrapped up in prompt*.c and do not depend on a client. These
functions are used to provide the original client prompt but also to
allow panes to have their own prompts, which works much much better for
floating panes. The mode prompts for both the tree modes and copy mode
are switched over to be per pane.

There are some visible changes (some of these may be changed if they
don't seem to be working well):

- Prompts in modes now appear in the bottom line, covering whatever
  content was there.

- command-prompt has a -P flag to open a pane prompt.

- Because they cover the content, the default style for prompts in modes
  now does not fill the entire line; the main command prompt stays the
  same.

- The old completion menu has gone, and completions are now shown after
  the text. Builtin aliases are no longer completed.

- Clicking the mouse on the prompt now moves the cursor or selects a
  completion.
This commit is contained in:
nicm
2026-06-25 11:39:11 +00:00
parent 6b1219b370
commit 51d037e881
19 changed files with 2811 additions and 1733 deletions

View File

@@ -102,6 +102,8 @@ SRCS= alerts.c \
popup.c \
proc.c \
procname.c \
prompt.c \
prompt-history.c \
regsub.c \
resize.c \
screen-redraw.c \

2
cfg.c
View File

@@ -57,7 +57,7 @@ cfg_done(__unused struct cmdq_item *item, __unused void *data)
if (cfg_item != NULL)
cmdq_continue(cfg_item);
status_prompt_load_history();
prompt_load_history();
return (CMD_RETURN_NORMAL);
}

View File

@@ -42,8 +42,8 @@ const struct cmd_entry cmd_command_prompt_entry = {
.name = "command-prompt",
.alias = NULL,
.args = { "1CbeFiklI:Np:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1CbeFiklN] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
.args = { "1CbeFiklI:NPp:t:T:", 0, 1, cmd_command_prompt_args_parse },
.usage = "[-1CbeFiklNP] [-I inputs] [-p prompts] " CMD_TARGET_CLIENT_USAGE
" [-T prompt-type] [template]",
.flags = CMD_CLIENT_TFLAG,
@@ -62,6 +62,8 @@ struct cmd_command_prompt_cdata {
int flags;
enum prompt_type prompt_type;
struct window_pane *wp;
struct cmd_command_prompt_prompt *prompts;
u_int count;
u_int current;
@@ -87,10 +89,15 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
struct cmd_command_prompt_cdata *cdata;
char *tmp, *prompts, *prompt, *next_prompt;
char *inputs = NULL, *next_input;
struct window_pane *wp = target->wp;
u_int count = args_count(args);
int wait = !args_has(args, 'b'), space = 1;
int pane = args_has(args, 'P');
if (tc->prompt_string != NULL)
if (pane) {
if (wp == NULL || window_pane_has_prompt(wp))
return (CMD_RETURN_NORMAL);
} else if (tc->prompt != NULL)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'i'))
wait = 0;
@@ -98,6 +105,8 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata = xcalloc(1, sizeof *cdata);
if (wait)
cdata->item = item;
if (pane)
cdata->wp = wp;
cdata->state = args_make_commands_prepare(self, item, 0, "%1", wait,
args_has(args, 'F'));
@@ -146,7 +155,7 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
}
if ((type = args_get(args, 'T')) != NULL) {
cdata->prompt_type = status_prompt_type(type);
cdata->prompt_type = prompt_type(type);
if (cdata->prompt_type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "unknown type: %s", type);
cmd_command_prompt_free(cdata);
@@ -167,9 +176,18 @@ cmd_command_prompt_exec(struct cmd *self, struct cmdq_item *item)
cdata->flags |= PROMPT_BSPACE_EXIT;
if (args_has(args, 'C'))
cdata->flags |= PROMPT_NOFREEZE;
status_prompt_set(tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags, cdata->prompt_type);
if (pane) {
cdata->flags |= PROMPT_ISPANE;
window_pane_set_prompt(wp, tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags,
cdata->prompt_type);
} else {
status_prompt_set(tc, target, cdata->prompts[0].prompt,
cdata->prompts[0].input, cmd_command_prompt_callback,
cmd_command_prompt_free, cdata, cdata->flags,
cdata->prompt_type);
}
if (!wait)
return (CMD_RETURN_NORMAL);
@@ -197,7 +215,12 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
cmd_append_argv(&cdata->argc, &cdata->argv, s);
if (++cdata->current != cdata->count) {
prompt = &cdata->prompts[cdata->current];
status_prompt_update(c, prompt->prompt, prompt->input);
if (cdata->wp != NULL) {
window_pane_update_prompt(cdata->wp,
prompt->prompt, prompt->input);
} else
status_prompt_update(c, prompt->prompt,
prompt->input);
return (PROMPT_CONTINUE);
}
}
@@ -225,12 +248,19 @@ cmd_command_prompt_callback(struct client *c, void *data, const char *s,
}
cmd_free_argv(argc, argv);
if (c->prompt_inputcb != cmd_command_prompt_callback)
/*
* An incremental prompt fires its callback on every edit but must stay
* open for further typing; only an explicit close (handled above) ends
* it.
*/
if (cdata->flags & PROMPT_INCREMENTAL)
return (PROMPT_CONTINUE);
out:
if (item != NULL)
if (item != NULL) {
cdata->item = NULL;
cmdq_continue(item);
}
return (PROMPT_CLOSE);
}
@@ -240,6 +270,11 @@ cmd_command_prompt_free(void *data)
struct cmd_command_prompt_cdata *cdata = data;
u_int i;
if (cdata->item != NULL) {
cmdq_continue(cdata->item);
cdata->item = NULL;
}
for (i = 0; i < cdata->count; i++) {
free(cdata->prompts[i].prompt);
free(cdata->prompts[i].input);

View File

@@ -279,7 +279,7 @@ cmd_display_panes_draw(struct client *c, __unused void *data)
tty_window_offset(&c->tty, &ctx.ox, &ctx.oy, &ctx.sx, &ctx.sy);
if (options_get_number(s->options, "status-position") == 0) {
lines = status_line_size(c);
if (c->message_string != NULL || c->prompt_string != NULL)
if (c->message_string != NULL || c->prompt != NULL)
lines = (lines == 0 ? 1 : lines);
ctx.statuslines = lines;
ctx.statustop = 1;

View File

@@ -55,56 +55,44 @@ 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;
u_int t, h;
const char *v;
if (cmd_get_entry(self) == &cmd_clear_prompt_history_entry) {
if (typestr == NULL) {
for (tidx = 0; tidx < PROMPT_NTYPES; tidx++) {
for (hidx = 0; hidx < status_prompt_hsize[tidx];
hidx++)
free(status_prompt_hlist[tidx][hidx]);
free(status_prompt_hlist[tidx]);
status_prompt_hlist[tidx] = NULL;
status_prompt_hsize[tidx] = 0;
}
for (t = 0; t < PROMPT_NTYPES; t++)
prompt_history_clear(t);
} else {
type = status_prompt_type(typestr);
type = prompt_type(typestr);
if (type == PROMPT_TYPE_INVALID) {
cmdq_error(item, "invalid type: %s", typestr);
return (CMD_RETURN_ERROR);
}
for (hidx = 0; hidx < status_prompt_hsize[type]; hidx++)
free(status_prompt_hlist[type][hidx]);
free(status_prompt_hlist[type]);
status_prompt_hlist[type] = NULL;
status_prompt_hsize[type] = 0;
prompt_history_clear(type);
}
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]);
for (t = 0; t < PROMPT_NTYPES; t++) {
typestr = prompt_type_string(t);
cmdq_print(item, "History for %s:\n", typestr);
for (h = 0; h < prompt_history_size(t); h++) {
v = prompt_history_get(t, h);
cmdq_print(item, "%d: %s", h + 1, v);
}
cmdq_print(item, "%s", "");
}
} else {
type = status_prompt_type(typestr);
type = 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, "History for %s:\n", prompt_type_string(type));
for (h = 0; h < prompt_history_size(type); h++) {
v = prompt_history_get(type, h);
cmdq_print(item, "%d: %s", h + 1, v);
}
cmdq_print(item, "%s", "");
}

View File

@@ -514,8 +514,8 @@ key_bindings_init(void)
"bind -Tcopy-mode M-l { send -X cursor-centre-horizontal }",
"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 -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-r { command-prompt -P -T search -ip'(search up)' -I'#{pane_search_string}' { send -X search-backward-incremental -- '%%' } }",
"bind -Tcopy-mode C-s { command-prompt -P -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 }",
@@ -523,18 +523,18 @@ key_bindings_init(void)
"bind -Tcopy-mode Space { send -X page-down }",
"bind -Tcopy-mode , { send -X jump-reverse }",
"bind -Tcopy-mode \\; { send -X jump-again }",
"bind -Tcopy-mode F { command-prompt -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode N { send -X search-reverse }",
"bind -Tcopy-mode P { send -X toggle-position }",
"bind -Tcopy-mode R { send -X rectangle-toggle }",
"bind -Tcopy-mode T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode X { send -X set-mark }",
"bind -Tcopy-mode f { command-prompt -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode g { command-prompt -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode f { command-prompt -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode g { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode n { send -X search-again }",
"bind -Tcopy-mode q { send -X cancel }",
"bind -Tcopy-mode r { send -X refresh-toggle }",
"bind -Tcopy-mode t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode Home { send -X start-of-line }",
"bind -Tcopy-mode End { send -X end-of-line }",
"bind -Tcopy-mode MouseDown1Pane select-pane",
@@ -550,15 +550,15 @@ key_bindings_init(void)
"bind -Tcopy-mode Down { send -X cursor-down }",
"bind -Tcopy-mode Left { send -X cursor-left }",
"bind -Tcopy-mode Right { send -X cursor-right }",
"bind -Tcopy-mode M-1 { command-prompt -Np'(repeat)' -I1 { send -N '%%' } }",
"bind -Tcopy-mode M-2 { command-prompt -Np'(repeat)' -I2 { send -N '%%' } }",
"bind -Tcopy-mode M-3 { command-prompt -Np'(repeat)' -I3 { send -N '%%' } }",
"bind -Tcopy-mode M-4 { command-prompt -Np'(repeat)' -I4 { send -N '%%' } }",
"bind -Tcopy-mode M-5 { command-prompt -Np'(repeat)' -I5 { send -N '%%' } }",
"bind -Tcopy-mode M-6 { command-prompt -Np'(repeat)' -I6 { send -N '%%' } }",
"bind -Tcopy-mode M-7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode M-8 { command-prompt -Np'(repeat)' -I8 { send -N '%%' } }",
"bind -Tcopy-mode M-9 { command-prompt -Np'(repeat)' -I9 { send -N '%%' } }",
"bind -Tcopy-mode M-1 { command-prompt -P -Np'(repeat)' -I1 { send -N '%%' } }",
"bind -Tcopy-mode M-2 { command-prompt -P -Np'(repeat)' -I2 { send -N '%%' } }",
"bind -Tcopy-mode M-3 { command-prompt -P -Np'(repeat)' -I3 { send -N '%%' } }",
"bind -Tcopy-mode M-4 { command-prompt -P -Np'(repeat)' -I4 { send -N '%%' } }",
"bind -Tcopy-mode M-5 { command-prompt -P -Np'(repeat)' -I5 { send -N '%%' } }",
"bind -Tcopy-mode M-6 { command-prompt -P -Np'(repeat)' -I6 { send -N '%%' } }",
"bind -Tcopy-mode M-7 { command-prompt -P -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode M-8 { command-prompt -P -Np'(repeat)' -I8 { send -N '%%' } }",
"bind -Tcopy-mode M-9 { command-prompt -P -Np'(repeat)' -I9 { send -N '%%' } }",
"bind -Tcopy-mode M-< { send -X history-top }",
"bind -Tcopy-mode M-> { send -X history-bottom }",
"bind -Tcopy-mode M-R { send -X top-line }",
@@ -599,25 +599,25 @@ 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 -T search -p'(search down)' { send -X search-forward -- '%%' } }",
"bind -Tcopy-mode-vi / { command-prompt -P -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 '%%' } }",
"bind -Tcopy-mode-vi 3 { command-prompt -Np'(repeat)' -I3 { send -N '%%' } }",
"bind -Tcopy-mode-vi 4 { command-prompt -Np'(repeat)' -I4 { send -N '%%' } }",
"bind -Tcopy-mode-vi 5 { command-prompt -Np'(repeat)' -I5 { send -N '%%' } }",
"bind -Tcopy-mode-vi 6 { command-prompt -Np'(repeat)' -I6 { send -N '%%' } }",
"bind -Tcopy-mode-vi 7 { command-prompt -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode-vi 8 { command-prompt -Np'(repeat)' -I8 { 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 1 { command-prompt -P -Np'(repeat)' -I1 { send -N '%%' } }",
"bind -Tcopy-mode-vi 2 { command-prompt -P -Np'(repeat)' -I2 { send -N '%%' } }",
"bind -Tcopy-mode-vi 3 { command-prompt -P -Np'(repeat)' -I3 { send -N '%%' } }",
"bind -Tcopy-mode-vi 4 { command-prompt -P -Np'(repeat)' -I4 { send -N '%%' } }",
"bind -Tcopy-mode-vi 5 { command-prompt -P -Np'(repeat)' -I5 { send -N '%%' } }",
"bind -Tcopy-mode-vi 6 { command-prompt -P -Np'(repeat)' -I6 { send -N '%%' } }",
"bind -Tcopy-mode-vi 7 { command-prompt -P -Np'(repeat)' -I7 { send -N '%%' } }",
"bind -Tcopy-mode-vi 8 { command-prompt -P -Np'(repeat)' -I8 { send -N '%%' } }",
"bind -Tcopy-mode-vi 9 { command-prompt -P -Np'(repeat)' -I9 { send -N '%%' } }",
"bind -Tcopy-mode-vi : { command-prompt -P -p'(goto line)' { send -X goto-line -- '%%' } }",
"bind -Tcopy-mode-vi \\; { send -X jump-again }",
"bind -Tcopy-mode-vi ? { command-prompt -T search -p'(search up)' { send -X search-backward -- '%%' } }",
"bind -Tcopy-mode-vi ? { command-prompt -P -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-pipe-end-of-line-and-cancel }",
"bind -Tcopy-mode-vi E { send -X next-space-end }",
"bind -Tcopy-mode-vi F { command-prompt -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode-vi F { command-prompt -P -1p'(jump backward)' { send -X jump-backward -- '%%' } }",
"bind -Tcopy-mode-vi G { send -X history-bottom }",
"bind -Tcopy-mode-vi H { send -X top-line }",
"bind -Tcopy-mode-vi J { send -X scroll-down }",
@@ -626,14 +626,14 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi M { send -X middle-line }",
"bind -Tcopy-mode-vi N { send -X search-reverse }",
"bind -Tcopy-mode-vi P { send -X toggle-position }",
"bind -Tcopy-mode-vi T { command-prompt -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode-vi T { command-prompt -P -1p'(jump to backward)' { send -X jump-to-backward -- '%%' } }",
"bind -Tcopy-mode-vi V { send -X select-line }",
"bind -Tcopy-mode-vi W { send -X next-space }",
"bind -Tcopy-mode-vi X { send -X set-mark }",
"bind -Tcopy-mode-vi ^ { send -X back-to-indentation }",
"bind -Tcopy-mode-vi b { send -X previous-word }",
"bind -Tcopy-mode-vi e { send -X next-word-end }",
"bind -Tcopy-mode-vi f { command-prompt -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode-vi f { command-prompt -P -1p'(jump forward)' { send -X jump-forward -- '%%' } }",
"bind -Tcopy-mode-vi g { send -X history-top }",
"bind -Tcopy-mode-vi h { send -X cursor-left }",
"bind -Tcopy-mode-vi j { send -X cursor-down }",
@@ -644,7 +644,7 @@ key_bindings_init(void)
"bind -Tcopy-mode-vi o { send -X other-end }",
"bind -Tcopy-mode-vi q { send -X cancel }",
"bind -Tcopy-mode-vi r { send -X refresh-toggle }",
"bind -Tcopy-mode-vi t { command-prompt -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode-vi t { command-prompt -P -1p'(jump to forward)' { send -X jump-to-forward -- '%%' } }",
"bind -Tcopy-mode-vi v { send -X rectangle-toggle }",
"bind -Tcopy-mode-vi w { send -X next-word }",
"bind -Tcopy-mode-vi '{' { send -X previous-paragraph }",

View File

@@ -37,6 +37,7 @@ enum mode_tree_preview {
};
struct mode_tree_item;
struct mode_tree_prompt;
TAILQ_HEAD(mode_tree_list, mode_tree_item);
struct mode_tree_data {
@@ -51,11 +52,11 @@ struct mode_tree_data {
struct sort_criteria sort_crit;
const char *view_name;
mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb;
mode_tree_search_cb searchcb;
mode_tree_menu_cb menucb;
mode_tree_height_cb heightcb;
mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb;
mode_tree_search_cb searchcb;
mode_tree_menu_cb menucb;
mode_tree_height_cb heightcb;
mode_tree_key_cb keycb;
mode_tree_swap_cb swapcb;
mode_tree_sort_cb sortcb;
@@ -77,6 +78,10 @@ struct mode_tree_data {
u_int current;
struct screen screen;
struct prompt *prompt;
struct mode_tree_prompt *prompt_data;
u_int prompt_cx;
int prompt_top;
int preview;
char *search;
@@ -124,9 +129,25 @@ struct mode_tree_menu {
u_int line;
};
static void mode_tree_free_items(struct mode_tree_list *);
static void mode_tree_draw_help(struct mode_tree_data *,
struct screen_write_ctx *);
/*
* Wrapper around a prompt owned by a mode tree. The mode tree holds a reference
* while the prompt is alive; the wrapper callbacks forward to the caller's
* callbacks and drop that reference when the prompt is freed.
*/
struct mode_tree_prompt {
struct mode_tree_data *mtd;
struct client *c;
mode_tree_prompt_input_cb inputcb;
prompt_free_cb freecb;
void *data;
};
static void mode_tree_free_items(struct mode_tree_list *);
static void mode_tree_draw_help(struct mode_tree_data *,
struct screen_write_ctx *);
static void mode_tree_draw_prompt(struct mode_tree_data *,
struct screen_write_ctx *);
static enum cmd_retval mode_tree_prompt_accept(struct cmdq_item *, void *);
static const struct menu_item mode_tree_menu_items[] = {
{ "Scroll Left", '<', NULL },
@@ -650,6 +671,7 @@ mode_tree_free(struct mode_tree_data *mtd)
if (mtd->zoomed == 0)
server_unzoom_window(wp->window);
mode_tree_clear_prompt(mtd);
mode_tree_free_items(&mtd->children);
mode_tree_clear_lines(mtd);
screen_free(&mtd->screen);
@@ -942,10 +964,148 @@ mode_tree_draw(struct mode_tree_data *mtd)
done:
if (mtd->help)
mode_tree_draw_help(mtd, &ctx);
screen_write_cursormove(&ctx, 0, mtd->current - mtd->offset, 0);
if (mtd->prompt != NULL)
mode_tree_draw_prompt(mtd, &ctx);
else {
s->mode &= ~MODE_CURSOR;
screen_write_cursormove(&ctx, 0, mtd->current - mtd->offset, 0);
}
screen_write_stop(&ctx);
}
static void
mode_tree_draw_prompt(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
{
struct screen *s = &mtd->screen;
struct prompt_draw_data pdd;
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int py;
if (sx == 0 || sy == 0)
return;
if (mtd->prompt_top)
py = 0;
else
py = sy - 1;
pdd.ctx = ctx;
pdd.cursor_x = &mtd->prompt_cx;
pdd.area_x = 0;
pdd.area_width = sx;
pdd.prompt_line = py;
s->mode |= MODE_CURSOR;
prompt_draw(mtd->prompt, &pdd);
screen_write_cursormove(ctx, mtd->prompt_cx, py, 0);
}
void
mode_tree_clear_prompt(struct mode_tree_data *mtd)
{
struct prompt *prompt = mtd->prompt;
if (mtd->prompt != NULL) {
mtd->prompt = NULL;
prompt_free(prompt);
mtd->screen.mode &= ~MODE_CURSOR;
}
}
int
mode_tree_has_prompt(struct mode_tree_data *mtd)
{
return (mtd->prompt != NULL);
}
static enum cmd_retval
mode_tree_prompt_accept(struct cmdq_item *item, void *data)
{
struct mode_tree_data *mtd = data;
struct client *c = cmdq_get_client(item);
key_code key = 'y';
if (mtd->prompt != NULL && c != NULL)
mode_tree_key(mtd, c, &key, NULL, NULL, NULL);
mode_tree_remove_ref(mtd);
return (CMD_RETURN_NORMAL);
}
static enum prompt_result
mode_tree_prompt_input_callback(void *data, const char *s,
enum prompt_key_result key)
{
struct mode_tree_prompt *mtp = data;
if (mtp->inputcb != NULL)
return (mtp->inputcb(mtp->c, mtp->data, s, key));
return (PROMPT_CLOSE);
}
static void
mode_tree_prompt_free_callback(void *data)
{
struct mode_tree_prompt *mtp = data;
if (mtp->mtd->prompt_data == mtp)
mtp->mtd->prompt_data = NULL;
if (mtp->freecb != NULL)
mtp->freecb(mtp->data);
mode_tree_remove_ref(mtp->mtd);
free(mtp);
}
void
mode_tree_set_prompt(struct mode_tree_data *mtd, struct client *c,
const char *prompt, const char *input, enum prompt_type type, int flags,
mode_tree_prompt_input_cb inputcb, prompt_free_cb freecb, void *data)
{
struct session *s;
struct options *oo;
struct prompt_create_data pd;
struct mode_tree_prompt *mtp;
if (c != NULL && c->session != NULL) {
s = c->session;
oo = s->options;
} else {
s = NULL;
oo = global_s_options;
}
mode_tree_clear_prompt(mtd);
mtp = xcalloc(1, sizeof *mtp);
mtp->mtd = mtd;
mtp->inputcb = inputcb;
mtp->freecb = freecb;
mtp->data = data;
mtd->references++;
mtd->prompt_top = options_get_number(oo, "status-position") == 0;
memset(&pd, 0, sizeof pd);
prompt_set_options(&pd, s);
pd.prompt = prompt;
pd.input = input;
pd.type = type;
pd.flags = flags|PROMPT_ISMODE;
pd.inputcb = mode_tree_prompt_input_callback;
pd.freecb = mode_tree_prompt_free_callback;
pd.data = mtp;
mtd->prompt = prompt_create(&pd);
mtd->prompt_data = mtp;
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
if ((flags & PROMPT_SINGLE) && (flags & PROMPT_ACCEPT) && c != NULL) {
mtd->references++;
cmdq_append(c, cmdq_get_callback(mode_tree_prompt_accept, mtd));
}
}
static struct mode_tree_item *
mode_tree_search_backward(struct mode_tree_data *mtd)
{
@@ -1089,12 +1249,6 @@ mode_tree_search_callback(__unused struct client *c, void *data, const char *s,
return (PROMPT_CLOSE);
}
static void
mode_tree_search_free(void *data)
{
mode_tree_remove_ref(data);
}
static enum prompt_result
mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
enum prompt_key_result key)
@@ -1120,12 +1274,6 @@ mode_tree_filter_callback(__unused struct client *c, void *data, const char *s,
return (PROMPT_CLOSE);
}
static void
mode_tree_filter_free(void *data)
{
mode_tree_remove_ref(data);
}
static void
mode_tree_clear_filter(struct mode_tree_data *mtd)
{
@@ -1225,7 +1373,7 @@ mode_tree_draw_help(struct mode_tree_data *mtd, struct screen_write_ctx *ctx)
struct grid_cell gc;
const char **line, **lines = NULL, *item = "item";
u_int sx = screen_size_x(s), sy = screen_size_y(s);
u_int x, y, w, h = 0, box_w, box_h;
u_int x, y, w, h = 0, box_w, box_h;
if (mtd->helpcb == NULL)
w = MODE_TREE_HELP_DEFAULT_WIDTH;
@@ -1275,14 +1423,66 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
{
struct mode_tree_line *line;
struct mode_tree_item *current, *parent, *mti;
u_int i, x, y;
u_int i, x, y, py, sx;
int choice, preview;
enum prompt_key_result result;
int redraw;
struct prompt *prompt;
struct mode_tree_prompt *mtp;
if (mtd->line_size == 0) {
*key = KEYC_NONE;
return (1);
}
if (mtd->prompt != NULL) {
redraw = 0;
prompt = mtd->prompt;
mtp = mtd->prompt_data;
if (mtp != NULL)
mtp->c = c;
if (KEYC_IS_MOUSE(*key)) {
if (m == NULL ||
MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1 ||
MOUSE_DRAG(m->b) || MOUSE_RELEASE(m->b) ||
cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0)
result = PROMPT_KEY_NOT_HANDLED;
else {
sx = screen_size_x(&mtd->screen);
if (mtd->prompt_top)
py = 0;
else
py = screen_size_y(&mtd->screen) - 1;
if (y == py) {
result = prompt_mouse(prompt, x, 0, sx,
&redraw);
} else
result = PROMPT_KEY_NOT_HANDLED;
}
} else
result = prompt_key(prompt, *key, &redraw);
if (mtd->prompt_data == mtp && mtp != NULL)
mtp->c = NULL;
/*
* Only an explicit close or the prompt marking itself closed
* ends it; cursor movement and editing keep it open.
*/
if (mtd->prompt == prompt &&
(result == PROMPT_KEY_CLOSE || prompt_closed(prompt)))
mode_tree_clear_prompt(mtd);
if (redraw || mtd->prompt != prompt) {
mode_tree_draw(mtd);
mtd->wp->flags |= PANE_REDRAW;
}
if (result != PROMPT_KEY_NOT_HANDLED) {
*key = KEYC_NONE;
return (0);
}
}
if (mtd->help) {
if (KEYC_IS_MOUSE(*key)) {
*key = KEYC_NONE;
@@ -1489,11 +1689,10 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
case '?':
case '/':
case 's'|KEYC_CTRL:
mtd->references++;
mtd->search_dir = MODE_TREE_SEARCH_FORWARD;
status_prompt_set(c, NULL, "(search) ", "",
mode_tree_search_callback, mode_tree_search_free, mtd,
PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
mode_tree_set_prompt(mtd, c, "(search) ", "",
PROMPT_TYPE_SEARCH, PROMPT_NOFORMAT,
mode_tree_search_callback, NULL, mtd);
break;
case 'n':
mtd->search_dir = MODE_TREE_SEARCH_FORWARD;
@@ -1504,12 +1703,12 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
mode_tree_search_set(mtd);
break;
case 'f':
mtd->references++;
status_prompt_set(c, NULL, "(filter) ", mtd->filter,
mode_tree_filter_callback, mode_tree_filter_free, mtd,
PROMPT_NOFORMAT, PROMPT_TYPE_SEARCH);
mode_tree_set_prompt(mtd, c, "(filter) ", mtd->filter,
PROMPT_TYPE_SEARCH, PROMPT_NOFORMAT,
mode_tree_filter_callback, NULL, mtd);
break;
case 'c':
mode_tree_clear_prompt(mtd);
mode_tree_clear_filter(mtd);
break;
case 'v':

View File

@@ -764,7 +764,9 @@ const struct options_table_entry options_table[] = {
{ .name = "message-style",
.type = OPTIONS_TABLE_STRING,
.scope = OPTIONS_TABLE_SESSION,
.default_str = "bg=yellow,fg=black,fill=yellow",
.default_str = "bg=yellow,fg=black,"
"#{?#{m/r:(^|#,)IS(PANE|MODE)($|#,),#{prompt_flags}},,"
"fill=yellow}",
.flags = OPTIONS_TABLE_IS_STYLE,
.separator = ",",
.text = "Style of messages and the command prompt. "

264
prompt-history.c Normal file
View File

@@ -0,0 +1,264 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2026 Nicholas Marriott <nicholas.marriott@gmail.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 <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "tmux.h"
static char *prompt_find_history_file(void);
static void prompt_add_typed_history(char *);
/* Prompt history. */
static char **prompt_hlist[PROMPT_NTYPES];
static u_int prompt_hsize[PROMPT_NTYPES];
/* Find the history file to load/save from/to. */
static char *
prompt_find_history_file(void)
{
const char *home, *history_file;
char *path;
history_file = options_get_string(global_options, "history-file");
if (*history_file == '\0')
return (NULL);
if (*history_file == '/')
return (xstrdup(history_file));
if (history_file[0] != '~' || history_file[1] != '/')
return (NULL);
if ((home = find_home()) == NULL)
return (NULL);
xasprintf(&path, "%s%s", home, history_file + 1);
return (path);
}
/* Add loaded history item to the appropriate list. */
static void
prompt_add_typed_history(char *line)
{
char *typestr;
enum prompt_type type = PROMPT_TYPE_INVALID;
typestr = strsep(&line, ":");
if (line != NULL)
type = 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) = ':';
prompt_add_history(typestr, PROMPT_TYPE_COMMAND);
} else
prompt_add_history(line, type);
}
/* Load prompt history from file. */
void
prompt_load_history(void)
{
FILE *f;
char *history_file, *line, *tmp;
size_t length;
if ((history_file = prompt_find_history_file()) == NULL)
return;
log_debug("loading history from %s", history_file);
f = fopen(history_file, "r");
if (f == NULL) {
log_debug("%s: %s", history_file, strerror(errno));
free(history_file);
return;
}
free(history_file);
for (;;) {
if ((line = fgetln(f, &length)) == NULL)
break;
if (length > 0) {
if (line[length - 1] == '\n') {
line[length - 1] = '\0';
prompt_add_typed_history(line);
} else {
tmp = xmalloc(length + 1);
memcpy(tmp, line, length);
tmp[length] = '\0';
prompt_add_typed_history(tmp);
free(tmp);
}
}
}
fclose(f);
}
/* Save prompt history to file. */
void
prompt_save_history(void)
{
FILE *f;
u_int i, type;
char *history_file;
if ((history_file = prompt_find_history_file()) == NULL)
return;
log_debug("saving history to %s", history_file);
f = fopen(history_file, "w");
if (f == NULL) {
log_debug("%s: %s", history_file, strerror(errno));
free(history_file);
return;
}
free(history_file);
for (type = 0; type < PROMPT_NTYPES; type++) {
for (i = 0; i < prompt_hsize[type]; i++) {
fputs(prompt_type_string(type), f);
fputc(':', f);
fputs(prompt_hlist[type][i], f);
fputc('\n', f);
}
}
fclose(f);
}
/* Get previous line from the history. */
const char *
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 (type >= PROMPT_NTYPES)
return (NULL);
if (prompt_hsize[type] == 0 || idx[type] == prompt_hsize[type])
return (NULL);
idx[type]++;
return (prompt_hlist[type][prompt_hsize[type] - idx[type]]);
}
/* Get next line from the history. */
const char *
prompt_down_history(u_int *idx, u_int type)
{
if (type >= PROMPT_NTYPES)
return ("");
if (prompt_hsize[type] == 0 || idx[type] == 0)
return ("");
idx[type]--;
if (idx[type] == 0)
return ("");
return (prompt_hlist[type][prompt_hsize[type] - idx[type]]);
}
/* Add line to the history. */
void
prompt_add_history(const char *line, u_int type)
{
u_int i, oldsize, newsize, freecount, hlimit, new = 1;
size_t movesize;
if (type >= PROMPT_NTYPES)
return;
oldsize = prompt_hsize[type];
if (oldsize > 0 &&
strcmp(prompt_hlist[type][oldsize - 1], line) == 0)
new = 0;
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(prompt_hlist[type][i]);
movesize = (oldsize - freecount) *
sizeof *prompt_hlist[type];
if (movesize > 0) {
memmove(&prompt_hlist[type][0],
&prompt_hlist[type][freecount], movesize);
}
}
if (newsize == 0) {
free(prompt_hlist[type]);
prompt_hlist[type] = NULL;
} else if (newsize != oldsize) {
prompt_hlist[type] =
xreallocarray(prompt_hlist[type], newsize,
sizeof *prompt_hlist[type]);
}
if (new == 1 && newsize > 0)
prompt_hlist[type][newsize - 1] = xstrdup(line);
prompt_hsize[type] = newsize;
}
/* Get history size. */
u_int
prompt_history_size(enum prompt_type type)
{
if (type >= PROMPT_NTYPES)
return (0);
return (prompt_hsize[type]);
}
/* Get history entry. */
const char *
prompt_history_get(enum prompt_type type, u_int idx)
{
if (type >= PROMPT_NTYPES)
return (NULL);
if (idx >= prompt_hsize[type])
return (NULL);
return (prompt_hlist[type][idx]);
}
/* Clear prompt history. */
void
prompt_history_clear(enum prompt_type type)
{
u_int idx;
if (type >= PROMPT_NTYPES)
return;
for (idx = 0; idx < prompt_hsize[type]; idx++)
free(prompt_hlist[type][idx]);
free(prompt_hlist[type]);
prompt_hlist[type] = NULL;
prompt_hsize[type] = 0;
}

1595
prompt.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1505,6 +1505,62 @@ redraw_set_draw_context(struct redraw_draw_ctx *dctx,
dctx->flags |= REDRAW_ISOLATES;
}
/* Draw a pane's prompt over its content. */
static void
redraw_draw_pane_prompt(struct redraw_draw_ctx *dctx, struct window_pane *wp)
{
struct redraw_scene *scene = dctx->scene;
struct client *c = scene->c;
struct tty *tty = &c->tty;
struct screen screen;
struct screen_write_ctx ctx;
struct prompt_draw_data pdd;
int ox = scene->ox, oy = scene->oy;
int sx = scene->sx, sy = scene->sy;
int line, cy, px, offset, width, wy;
if (wp->prompt == NULL || wp->sx == 0 || wp->sy == 0)
return;
if (~dctx->flags & REDRAW_STATUS_TOP)
wy = wp->yoff + (int)wp->sy - 1;
else
wy = wp->yoff;
if (wy < oy || wy >= oy + sy)
return;
line = wy - oy;
if (dctx->flags & REDRAW_STATUS_TOP)
cy = dctx->status_lines + line;
else
cy = line;
if (wp->xoff + (int)wp->sx <= ox || wp->xoff >= ox + sx)
return;
if (wp->xoff < ox) {
offset = ox - wp->xoff;
px = 0;
} else {
offset = 0;
px = wp->xoff - ox;
}
width = wp->sx - offset;
if (px + width > sx)
width = sx - px;
screen_init(&screen, wp->sx, 1, 0);
screen_write_start(&ctx, &screen);
pdd.ctx = &ctx;
pdd.cursor_x = &wp->prompt_cx;
pdd.area_x = 0;
pdd.area_width = wp->sx;
pdd.prompt_line = 0;
prompt_draw(wp->prompt, &pdd);
screen_write_stop(&ctx);
tty_draw_line(tty, &screen, 0, offset, width, px, cy, NULL);
screen_free(&screen);
}
/* Draw scene to client. */
static void
redraw_draw(struct client *c, struct window_pane *wp, int flags)
@@ -1526,7 +1582,7 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
if (flags & REDRAW_STATUS) {
if (c->message_string != NULL)
redraw = status_message_redraw(c);
else if (c->prompt_string != NULL)
else if (c->prompt != NULL)
redraw = status_prompt_redraw(c);
else
redraw = status_redraw(c);
@@ -1600,9 +1656,20 @@ redraw_draw(struct client *c, struct window_pane *wp, int flags)
else
redraw_draw_lines(&dctx, flags);
if (flags & REDRAW_PANE) {
if (wp != NULL)
redraw_draw_pane_prompt(&dctx, wp);
else {
TAILQ_FOREACH(loop, &scene->w->panes, entry) {
if (window_pane_is_visible(loop))
redraw_draw_pane_prompt(&dctx, loop);
}
}
}
if (flags & REDRAW_STATUS) {
lines = dctx.status_lines;
if (c->message_string != NULL || c->prompt_string != NULL)
if (c->message_string != NULL || c->prompt != NULL)
lines = (lines == 0 ? 1 : lines);
if (dctx.flags & REDRAW_STATUS_TOP)
y = 0;

View File

@@ -489,10 +489,7 @@ server_client_lost(struct client *c)
free(c->message_string);
if (event_initialized(&c->message_timer))
evtimer_del(&c->message_timer);
free(c->prompt_saved);
free(c->prompt_string);
free(c->prompt_buffer);
prompt_free(c->prompt);
format_lost_client(c);
environ_free(c->environ);
@@ -1464,6 +1461,7 @@ server_client_handle_key0(struct client *c, struct key_event *event,
{
struct session *s = c->session;
struct cmdq_item *item;
struct window_pane *wp;
/* Check the client is good to accept input. */
if (s == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS))
@@ -1493,6 +1491,7 @@ server_client_handle_key0(struct client *c, struct key_event *event,
return (0);
status_message_clear(c);
}
if (c->overlay_key != NULL) {
switch (c->overlay_key(c, c->overlay_data, event)) {
case 0:
@@ -1502,9 +1501,10 @@ server_client_handle_key0(struct client *c, struct key_event *event,
return (0);
}
}
server_client_clear_overlay(c);
if (c->prompt_string != NULL) {
switch (status_prompt_key(c, event->key)) {
if (c->prompt != NULL) {
switch (status_prompt_key(c, event->key, &event->m)) {
case PROMPT_KEY_HANDLED:
case PROMPT_KEY_CLOSE:
return (0);
@@ -1513,6 +1513,29 @@ server_client_handle_key0(struct client *c, struct key_event *event,
break;
}
}
wp = s->curw->window->active;
if (wp == NULL || !window_pane_has_prompt(wp)) {
TAILQ_FOREACH(wp, &s->curw->window->panes, entry) {
if (window_pane_has_prompt(wp) &&
window_pane_is_visible(wp))
break;
}
}
if (wp != NULL &&
window_pane_has_prompt(wp) &&
window_pane_is_visible(wp)) {
switch (window_pane_prompt_key(wp, c, event->key, &event->m)) {
case PROMPT_KEY_HANDLED:
case PROMPT_KEY_CLOSE:
case PROMPT_KEY_MOVE:
return (0);
case PROMPT_KEY_NOT_HANDLED:
if (KEYC_IS_MOUSE(event->key))
return (0);
break;
}
}
}
/*
@@ -1784,6 +1807,42 @@ out:
bufferevent_enable(wp->event, EV_READ);
}
/* Move cursor for pane prompt. */
static int
server_client_prompt_cursor(struct client *c, struct window_pane *wp, int *mode,
u_int *cx, u_int *cy)
{
struct tty *tty = &c->tty;
struct visible_ranges *r;
u_int ox, oy, sx, sy;
int px, py;
if (!window_pane_has_prompt(wp))
return (0);
*mode &= ~MODE_CURSOR;
tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (status_at_line(c) == 0)
py = wp->yoff;
else
py = wp->yoff + wp->sy - 1;
px = wp->xoff + wp->prompt_cx;
if (px < (int)ox || px > (int)(ox + sx) ||
py < (int)oy || py > (int)(oy + sy))
return (1);
*cx = px - ox;
*cy = py - oy;
r = window_visible_ranges(wp, *cx, *cy, 1, NULL);
if (window_position_is_visible(r, *cx)) {
if (status_at_line(c) == 0)
*cy += status_line_size(c);
*mode |= MODE_CURSOR;
}
return (1);
}
/*
* Update cursor position and mode settings. The scroll region and attributes
* are cleared when idle (waiting for an event) as this is the most likely time
@@ -1802,7 +1861,7 @@ server_client_reset_state(struct client *c)
struct screen *s = NULL;
struct options *oo = c->session->options;
int mode = 0, cursor, flags, pane_mode = 0;
u_int cx = 0, cy = 0, ox, oy, sx, sy, n;
u_int cx = 0, cy = 0, ox, oy, sx, sy, prompt = 0;
struct visible_ranges *r;
if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED))
@@ -1816,7 +1875,7 @@ server_client_reset_state(struct client *c)
if (c->overlay_draw != NULL) {
if (c->overlay_mode != NULL)
s = c->overlay_mode(c, c->overlay_data, &cx, &cy);
} else if (wp != NULL && c->prompt_string == NULL)
} else if (wp != NULL && c->prompt == NULL)
s = wp->screen;
else
s = c->status.active;
@@ -1832,42 +1891,36 @@ server_client_reset_state(struct client *c)
tty_margin_off(tty);
/* Move cursor to pane cursor and offset. */
if (c->prompt_string != NULL) {
n = options_get_number(oo, "status-position");
if (n == 0)
cy = status_prompt_line_at(c);
else {
n = status_line_size(c) - status_prompt_line_at(c);
if (n <= tty->sy)
cy = tty->sy - n;
else
cy = tty->sy - 1;
}
cx = c->prompt_cursor;
if (c->prompt != NULL) {
prompt = 1;
status_prompt_cursor(c, &cx, &cy);
} else if (wp != NULL && c->overlay_draw == NULL) {
cursor = 0;
pane_mode = wp->base.mode;
prompt = server_client_prompt_cursor(c, wp, &mode, &cx, &cy);
if (!prompt) {
cursor = 0;
pane_mode = wp->base.mode;
tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (wp->xoff + (int)s->cx >= (int)ox &&
wp->xoff + (int)s->cx <= (int)ox + (int)sx &&
wp->yoff + (int)s->cy >= (int)oy &&
wp->yoff + (int)s->cy <= (int)oy + (int)sy) {
cursor = 1;
tty_window_offset(tty, &ox, &oy, &sx, &sy);
if (wp->xoff + (int)s->cx >= (int)ox &&
wp->xoff + (int)s->cx <= (int)ox + (int)sx &&
wp->yoff + (int)s->cy >= (int)oy &&
wp->yoff + (int)s->cy <= (int)oy + (int)sy) {
cursor = 1;
cx = wp->xoff + (int)s->cx - (int)ox;
cy = wp->yoff + (int)s->cy - (int)oy;
cx = wp->xoff + (int)s->cx - (int)ox;
cy = wp->yoff + (int)s->cy - (int)oy;
r = window_visible_ranges(wp, cx, cy, 1, NULL);
if (!window_position_is_visible(r, cx))
cursor = 0;
r = window_visible_ranges(wp, cx, cy, 1, NULL);
if (!window_position_is_visible(r, cx))
cursor = 0;
if (status_at_line(c) == 0)
cy += status_line_size(c);
if (status_at_line(c) == 0)
cy += status_line_size(c);
}
if ((pane_mode & MODE_SYNC) || !cursor)
mode &= ~MODE_CURSOR;
}
if ((pane_mode & MODE_SYNC) || !cursor)
mode &= ~MODE_CURSOR;
} else if (c->overlay_mode == NULL || s == NULL)
mode &= ~MODE_CURSOR;
if (~pane_mode & MODE_SYNC) {
@@ -1895,7 +1948,7 @@ server_client_reset_state(struct client *c)
}
/* Clear bracketed paste mode if at the prompt. */
if (c->overlay_draw == NULL && c->prompt_string != NULL)
if (c->overlay_draw == NULL && prompt)
mode &= ~MODE_BRACKETPASTE;
/* Set the terminal mode and reset attributes. */

View File

@@ -248,7 +248,7 @@ server_start(struct tmuxproc *client, uint64_t flags, struct event_base *base,
proc_loop(server_proc, server_loop);
job_kill_all();
status_prompt_save_history();
prompt_save_history();
exit(0);
}

1645
status.c

File diff suppressed because it is too large Load Diff

4
tmux.1
View File

@@ -7359,7 +7359,7 @@ See
for possible values for
.Ar prompt\-type .
.It Xo Ic command\-prompt
.Op Fl 1bCeFiklN
.Op Fl 1bCeFiklNP
.Op Fl I Ar inputs
.Op Fl p Ar prompts
.Op Fl t Ar target\-client
@@ -7439,6 +7439,8 @@ user exits the command prompt.
makes
.Em BSpace
cancel an empty prompt.
.Fl P
opens a prompt inside a pane instead of on the status line.
.Pp
.Fl T
tells

157
tmux.h
View File

@@ -62,6 +62,8 @@ struct mouse_event;
struct options;
struct options_array_item;
struct options_entry;
struct prompt;
struct window_pane_prompt;
struct redraw_scene;
struct redraw_span;
struct screen_write_citem;
@@ -1296,6 +1298,10 @@ struct window_pane {
char *searchstr;
int searchregex;
struct prompt *prompt;
struct window_pane_prompt *prompt_data;
u_int prompt_cx;
int border_gc_set;
struct grid_cell border_gc;
int active_border_gc_set;
@@ -1936,20 +1942,12 @@ struct status_line {
struct screen *active;
int references;
u_int prompt_cx;
struct grid_cell style;
struct style_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 *);
@@ -1990,6 +1988,14 @@ RB_HEAD(client_windows, client_window);
/* Maximum time to be pasting. */
#define CLIENT_PASTE_TIME_LIMIT 5
/* Prompt type. */
#define PROMPT_NTYPES 2
enum prompt_type {
PROMPT_TYPE_COMMAND,
PROMPT_TYPE_SEARCH,
PROMPT_TYPE_INVALID = 0xff
};
/* Prompt result. */
enum prompt_result {
PROMPT_CONTINUE,
@@ -2005,11 +2011,63 @@ enum prompt_key_result {
};
/* Prompt callbacks. */
typedef enum prompt_result (*prompt_input_cb)(struct client *, void *,
typedef enum prompt_result (*prompt_input_cb)(void *, const char *,
enum prompt_key_result);
typedef enum prompt_result (*status_prompt_input_cb)(struct client *, void *,
const char *, enum prompt_key_result);
typedef enum prompt_result (*mode_tree_prompt_input_cb)(struct client *, void *,
const char *, enum prompt_key_result);
typedef void (*prompt_free_cb)(void *);
/* Overlay callbacks. */
/* Prompt flags. */
#define PROMPT_SINGLE 0x1
#define PROMPT_NUMERIC 0x2
#define PROMPT_INCREMENTAL 0x4
#define PROMPT_NOFORMAT 0x8
#define PROMPT_KEY 0x10
#define PROMPT_ACCEPT 0x20
#define PROMPT_QUOTENEXT 0x40
#define PROMPT_BSPACE_EXIT 0x80
#define PROMPT_NOFREEZE 0x100
#define PROMPT_COMMANDMODE 0x200
#define PROMPT_ISPANE 0x400
#define PROMPT_ISMODE 0x800
/* Prompt create data. */
struct prompt_create_data {
struct cmd_find_state *fs;
const char *prompt;
const char *input;
enum prompt_type type;
int flags;
struct grid_cell style;
struct grid_cell command_style;
enum screen_cursor_style cstyle;
enum screen_cursor_style command_cstyle;
int ccolour;
int cmode;
int command_cmode;
const char *message_format;
int keys;
const char *word_separators;
prompt_input_cb inputcb;
prompt_free_cb freecb;
void *data;
};
/* Prompt draw data. */
struct prompt_draw_data {
struct screen_write_ctx *ctx;
u_int *cursor_x;
u_int area_x;
u_int area_width;
u_int prompt_line;
};
/* Overlay callbacks */
typedef struct visible_ranges *(*overlay_check_cb)(struct client *, void *,
u_int, u_int, u_int);
typedef struct screen *(*overlay_mode_cb)(struct client *, void *, u_int *,
@@ -2154,29 +2212,7 @@ struct client {
char *message_string;
struct event message_timer;
char *prompt_string;
struct utf8_data *prompt_buffer;
struct cmd_find_state prompt_state;
char *prompt_last;
size_t prompt_index;
prompt_input_cb prompt_inputcb;
prompt_free_cb prompt_freecb;
void *prompt_data;
u_int prompt_hindex[PROMPT_NTYPES];
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_ACCEPT 0x20
#define PROMPT_QUOTENEXT 0x40
#define PROMPT_BSPACE_EXIT 0x80
#define PROMPT_NOFREEZE 0x100
#define PROMPT_COMMANDMODE 0x200
int prompt_flags;
enum prompt_type prompt_type;
int prompt_cursor;
struct prompt *prompt;
struct session *session;
struct session *last_session;
@@ -3100,8 +3136,6 @@ 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 *);
@@ -3117,16 +3151,38 @@ void printflike(6, 7) status_message_set(struct client *, int, int, int, int,
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,
const char *, const char *, status_prompt_input_cb, prompt_free_cb,
void *, int, enum prompt_type);
void status_prompt_clear(struct client *);
int status_prompt_redraw(struct client *);
enum prompt_key_result status_prompt_key(struct client *, key_code);
void status_prompt_cursor(struct client *, u_int *, u_int *);
enum prompt_key_result status_prompt_key(struct client *, key_code,
struct mouse_event *);
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);
/* prompt.c */
void prompt_set_options(struct prompt_create_data *, struct session *);
struct prompt *prompt_create(const struct prompt_create_data *);
void prompt_free(struct prompt *);
void prompt_incremental_start(struct prompt *);
void prompt_draw(struct prompt *, struct prompt_draw_data *);
enum prompt_key_result prompt_key(struct prompt *, key_code, int *);
enum prompt_key_result prompt_mouse(struct prompt *, u_int, u_int, u_int,
int *);
void prompt_update(struct prompt *, const char *, const char *);
int prompt_closed(struct prompt *);
enum prompt_type prompt_type(const char *);
const char *prompt_type_string(enum prompt_type);
/* prompt-history.c */
const char *prompt_up_history(u_int *, u_int);
const char *prompt_down_history(u_int *, u_int);
void prompt_add_history(const char *, u_int);
u_int prompt_history_size(enum prompt_type);
const char *prompt_history_get(enum prompt_type, u_int);
void prompt_history_clear(enum prompt_type);
void prompt_load_history(void);
void prompt_save_history(void);
/* resize.c */
void resize_window(struct window *, u_int, u_int, int, int);
@@ -3452,6 +3508,16 @@ int window_pane_key(struct window_pane *, struct client *,
struct mouse_event *);
void window_pane_paste(struct window_pane *, key_code, char *,
size_t);
void window_pane_set_prompt(struct window_pane *, struct client *,
struct cmd_find_state *, const char *, const char *,
status_prompt_input_cb, prompt_free_cb, void *, int,
enum prompt_type);
void window_pane_clear_prompt(struct window_pane *);
int window_pane_has_prompt(struct window_pane *);
void window_pane_update_prompt(struct window_pane *, const char *,
const char *);
enum prompt_key_result window_pane_prompt_key(struct window_pane *,
struct client *, key_code, struct mouse_event *);
int window_pane_is_visible(struct window_pane *);
int window_pane_exited(struct window_pane *);
u_int window_pane_search(struct window_pane *, const char *, int,
@@ -3612,6 +3678,11 @@ void mode_tree_remove(struct mode_tree_data *, struct mode_tree_item *);
void mode_tree_draw(struct mode_tree_data *);
int mode_tree_key(struct mode_tree_data *, struct client *, key_code *,
struct mouse_event *, u_int *, u_int *);
void mode_tree_set_prompt(struct mode_tree_data *, struct client *,
const char *, const char *, enum prompt_type, int,
mode_tree_prompt_input_cb, prompt_free_cb, void *);
void mode_tree_clear_prompt(struct mode_tree_data *);
int mode_tree_has_prompt(struct mode_tree_data *);
void mode_tree_run_command(struct client *, struct cmd_find_state *,
const char *, const char *);

View File

@@ -1145,10 +1145,10 @@ window_customize_set_option(struct client *c,
new_item->idx = idx;
data->references++;
status_prompt_set(c, NULL, prompt, value,
mode_tree_set_prompt(data->data, c, prompt, value,
PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT,
window_customize_set_option_callback,
window_customize_free_item_callback, new_item,
PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
window_customize_free_item_callback, new_item);
free(prompt);
free(value);
@@ -1283,10 +1283,10 @@ window_customize_set_key(struct client *c,
new_item->key = key;
data->references++;
status_prompt_set(c, NULL, prompt, value,
mode_tree_set_prompt(data->data, c, prompt, value,
PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT,
window_customize_set_command_callback,
window_customize_free_item_callback, new_item,
PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
window_customize_free_item_callback, new_item);
free(prompt);
free(value);
} else if (strcmp(s, "Note") == 0) {
@@ -1299,11 +1299,11 @@ window_customize_set_key(struct client *c,
new_item->key = key;
data->references++;
status_prompt_set(c, NULL, prompt,
mode_tree_set_prompt(data->data, c, prompt,
(bd->note == NULL ? "" : bd->note),
PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT,
window_customize_set_note_callback,
window_customize_free_item_callback, new_item,
PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
window_customize_free_item_callback, new_item);
free(prompt);
}
}
@@ -1477,11 +1477,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
xasprintf(&prompt, "Reset %s to default? ", item->name);
data->references++;
data->change = WINDOW_CUSTOMIZE_RESET;
status_prompt_set(c, NULL, prompt, "",
window_customize_change_current_callback,
window_customize_free_callback, data,
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
window_customize_change_current_callback,
window_customize_free_callback, data);
free(prompt);
break;
case 'D':
@@ -1491,11 +1491,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
xasprintf(&prompt, "Reset %u tagged to default? ", tagged);
data->references++;
data->change = WINDOW_CUSTOMIZE_RESET;
status_prompt_set(c, NULL, prompt, "",
window_customize_change_tagged_callback,
window_customize_free_callback, data,
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
window_customize_change_tagged_callback,
window_customize_free_callback, data);
free(prompt);
break;
case 'u':
@@ -1508,11 +1508,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
xasprintf(&prompt, "Unset %s? ", item->name);
data->references++;
data->change = WINDOW_CUSTOMIZE_UNSET;
status_prompt_set(c, NULL, prompt, "",
window_customize_change_current_callback,
window_customize_free_callback, data,
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
window_customize_change_current_callback,
window_customize_free_callback, data);
free(prompt);
break;
case 'U':
@@ -1522,11 +1522,11 @@ window_customize_key(struct window_mode_entry *wme, struct client *c,
xasprintf(&prompt, "Unset %u tagged? ", tagged);
data->references++;
data->change = WINDOW_CUSTOMIZE_UNSET;
status_prompt_set(c, NULL, prompt, "",
window_customize_change_tagged_callback,
window_customize_free_callback, data,
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
window_customize_change_tagged_callback,
window_customize_free_callback, data);
free(prompt);
break;
case 'H':

View File

@@ -1317,10 +1317,11 @@ again:
if (prompt == NULL)
break;
data->references++;
status_prompt_set(c, NULL, prompt, "",
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
window_tree_kill_current_callback, window_tree_command_free,
data, PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
data);
free(prompt);
break;
case 'X':
@@ -1329,10 +1330,11 @@ again:
break;
xasprintf(&prompt, "Kill %u tagged? ", tagged);
data->references++;
status_prompt_set(c, NULL, prompt, "",
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND,
PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
window_tree_kill_tagged_callback, window_tree_command_free,
data, PROMPT_SINGLE|PROMPT_NOFORMAT|data->prompt_flags,
PROMPT_TYPE_COMMAND);
data);
free(prompt);
break;
case ':':
@@ -1342,9 +1344,10 @@ again:
else
xasprintf(&prompt, "(current) ");
data->references++;
status_prompt_set(c, NULL, prompt, "",
mode_tree_set_prompt(data->data, c, prompt, "",
PROMPT_TYPE_COMMAND, PROMPT_NOFORMAT,
window_tree_command_callback, window_tree_command_free,
data, PROMPT_NOFORMAT, PROMPT_TYPE_COMMAND);
data);
free(prompt);
break;
case '\r':

170
window.c
View File

@@ -80,6 +80,14 @@ RB_GENERATE(windows, window, entry, window_cmp);
RB_GENERATE(winlinks, winlink, entry, winlink_cmp);
RB_GENERATE(window_pane_tree, window_pane, tree_entry, window_pane_cmp);
struct window_pane_prompt {
u_int wp_id;
struct client *c;
status_prompt_input_cb inputcb;
prompt_free_cb freecb;
void *data;
};
int
window_cmp(struct window *w1, struct window *w2)
{
@@ -1148,6 +1156,8 @@ window_pane_destroy(struct window_pane *wp)
window_pane_wait_finish(wp);
spawn_editor_finish(wp);
window_pane_clear_prompt(wp);
window_pane_free_modes(wp);
free(wp->searchstr);
@@ -1360,6 +1370,7 @@ window_pane_reset_mode(struct window_pane *wp)
server_kill_pane(wp);
}
/* Reset all modes. */
void
window_pane_reset_mode_all(struct window_pane *wp)
{
@@ -1367,6 +1378,165 @@ window_pane_reset_mode_all(struct window_pane *wp)
window_pane_reset_mode(wp);
}
/* Prompt input callback. */
static enum prompt_result
window_pane_prompt_input_callback(void *data, const char *s,
enum prompt_key_result key)
{
struct window_pane_prompt *wpp = data;
if (wpp->inputcb != NULL)
return (wpp->inputcb(wpp->c, wpp->data, s, key));
return (PROMPT_CLOSE);
}
/* Prompt free callback. */
static void
window_pane_prompt_free_callback(void *data)
{
struct window_pane_prompt *wpp = data;
struct window_pane *wp;
wp = window_pane_find_by_id(wpp->wp_id);
if (wp != NULL && wp->prompt_data == wpp)
wp->prompt_data = NULL;
if (wpp->freecb != NULL)
wpp->freecb(wpp->data);
free(wpp);
}
/* Open a prompt owned by a pane, drawn over the pane instead of the status. */
void
window_pane_set_prompt(struct window_pane *wp, struct client *c,
struct cmd_find_state *fs, const char *msg, const char *input,
status_prompt_input_cb inputcb, prompt_free_cb freecb, void *data,
int flags, enum prompt_type type)
{
struct session *s = NULL;
struct prompt_create_data pd;
struct window_pane_prompt *wpp;
if (c != NULL)
s = c->session;
window_pane_clear_prompt(wp);
wpp = xcalloc(1, sizeof *wpp);
wpp->wp_id = wp->id;
wpp->c = c;
wpp->inputcb = inputcb;
wpp->freecb = freecb;
wpp->data = data;
memset(&pd, 0, sizeof pd);
prompt_set_options(&pd, s);
pd.fs = fs;
pd.prompt = msg;
pd.input = input;
pd.type = type;
pd.flags = flags;
pd.inputcb = window_pane_prompt_input_callback;
pd.freecb = window_pane_prompt_free_callback;
pd.data = wpp;
wp->prompt = prompt_create(&pd);
wp->prompt_data = wpp;
wp->flags |= PANE_REDRAW;
prompt_incremental_start(wp->prompt);
}
/* Close a pane prompt. */
void
window_pane_clear_prompt(struct window_pane *wp)
{
struct prompt *prompt = wp->prompt;
if (prompt == NULL)
return;
wp->prompt = NULL;
prompt_free(prompt);
wp->flags |= PANE_REDRAW;
}
/* Does this pane have an open prompt? */
int
window_pane_has_prompt(struct window_pane *wp)
{
return (wp->prompt != NULL);
}
/* Replace the message and input of an open pane prompt. */
void
window_pane_update_prompt(struct window_pane *wp, const char *msg,
const char *input)
{
if (wp->prompt != NULL) {
prompt_update(wp->prompt, msg, input);
wp->flags |= PANE_REDRAW;
}
}
/*
* Pass a key to a pane prompt. The client is set transiently for the duration
* of the key in case the prompt or pane is destroyed by the callback.
*/
enum prompt_key_result
window_pane_prompt_key(struct window_pane *wp, struct client *c, key_code key,
struct mouse_event *m)
{
struct prompt *prompt = wp->prompt;
struct window_pane_prompt *wpp = wp->prompt_data;
enum prompt_key_result result;
u_int wp_id = wp->id, x, y, py;
int redraw = 0;
if (prompt == NULL)
return (PROMPT_KEY_NOT_HANDLED);
if (wpp != NULL)
wpp->c = c;
if (KEYC_IS_MOUSE(key)) {
if (m == NULL ||
MOUSE_BUTTONS(m->b) != MOUSE_BUTTON_1 ||
MOUSE_DRAG(m->b) ||
MOUSE_RELEASE(m->b) ||
cmd_mouse_at(wp, m, &x, &y, 0) != 0)
result = PROMPT_KEY_NOT_HANDLED;
else {
if (c != NULL && status_at_line(c) == 0)
py = 0;
else
py = wp->sy - 1;
if (y == py) {
result = prompt_mouse(prompt, x, 0, wp->sx,
&redraw);
} else
result = PROMPT_KEY_NOT_HANDLED;
}
} else
result = prompt_key(prompt, key, &redraw);
wp = window_pane_find_by_id(wp_id);
if (wp == NULL)
return (result);
if (wpp != NULL && wp->prompt_data == wpp)
wpp->c = NULL;
/*
* Only an explicit close or the prompt marking itself closed ends it;
* cursor movement and editing keep it open.
*/
if (wp->prompt == prompt &&
(result == PROMPT_KEY_CLOSE || prompt_closed(prompt)))
window_pane_clear_prompt(wp);
if (redraw || wp->prompt != prompt)
wp->flags |= PANE_REDRAW;
return (result);
}
static void
window_pane_copy_paste(struct window_pane *wp, char *buf, size_t len)
{