Merge branch 'obsd-master'

This commit is contained in:
Thomas Adam
2026-06-25 13:29:53 +01:00
19 changed files with 2811 additions and 1733 deletions

View File

@@ -184,6 +184,8 @@ dist_tmux_SOURCES = \
paste.c \
popup.c \
proc.c \
prompt.c \
prompt-history.c \
regsub.c \
resize.c \
screen-redraw.c \

2
cfg.c
View File

@@ -56,7 +56,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

@@ -763,7 +763,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

@@ -485,10 +485,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);
@@ -1460,6 +1457,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))
@@ -1489,6 +1487,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:
@@ -1498,9 +1497,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);
@@ -1509,6 +1509,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;
}
}
}
/*
@@ -1780,6 +1803,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
@@ -1798,7 +1857,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))
@@ -1812,7 +1871,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;
@@ -1828,42 +1887,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) {
@@ -1891,7 +1944,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

@@ -254,7 +254,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

@@ -7361,7 +7361,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
@@ -7441,6 +7441,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

@@ -63,6 +63,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;
@@ -1330,6 +1332,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;
@@ -1977,20 +1983,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 *);
@@ -2031,6 +2029,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,
@@ -2046,11 +2052,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 *,
@@ -2195,29 +2253,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;
@@ -3145,8 +3181,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 *);
@@ -3162,16 +3196,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);
@@ -3501,6 +3557,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,
@@ -3661,6 +3727,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

@@ -78,6 +78,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)
{
@@ -1155,6 +1163,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);
@@ -1371,6 +1381,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)
{
@@ -1378,6 +1389,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)
{