diff --git a/alerts.c b/alerts.c index 4cc5c3eb..d3c5df05 100644 --- a/alerts.c +++ b/alerts.c @@ -314,10 +314,11 @@ alerts_set_message(struct winlink *wl, const char *type, const char *option) tty_putcode(&c->tty, TTYC_BEL); if (visual == VISUAL_OFF) continue; - if (c->session->curw == wl) - status_message_set(c, -1, 1, "%s in current window", type); - else { - status_message_set(c, -1, 1, "%s in window %d", type, + if (c->session->curw == wl) { + status_message_set(c, -1, 1, 0, "%s in current window", + type); + } else { + status_message_set(c, -1, 1, 0, "%s in window %d", type, wl->idx); } } diff --git a/cfg.c b/cfg.c index 3ef46e66..8ec5a95b 100644 --- a/cfg.c +++ b/cfg.c @@ -102,6 +102,7 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, struct cmd_parse_input pi; struct cmd_parse_result *pr; struct cmdq_item *new_item0; + struct cmdq_state *state; if (new_item != NULL) *new_item = NULL; @@ -135,12 +136,19 @@ load_cfg(const char *path, struct client *c, struct cmdq_item *item, int flags, return (0); } - new_item0 = cmdq_get_command(pr->cmdlist, NULL); + if (item != NULL) + state = cmdq_copy_state(cmdq_get_state(item)); + else + state = cmdq_new_state(NULL, NULL, 0); + cmdq_add_format(state, "current_file", "%s", pi.file); + + new_item0 = cmdq_get_command(pr->cmdlist, state); if (item != NULL) new_item0 = cmdq_insert_after(item, new_item0); else new_item0 = cmdq_append(NULL, new_item0); cmd_list_free(pr->cmdlist); + cmdq_free_state(state); if (new_item != NULL) *new_item = new_item0; @@ -155,6 +163,7 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path, struct cmd_parse_input pi; struct cmd_parse_result *pr; struct cmdq_item *new_item0; + struct cmdq_state *state; if (new_item != NULL) *new_item = NULL; @@ -181,12 +190,19 @@ load_cfg_from_buffer(const void *buf, size_t len, const char *path, return (0); } - new_item0 = cmdq_get_command(pr->cmdlist, NULL); + if (item != NULL) + state = cmdq_copy_state(cmdq_get_state(item)); + else + state = cmdq_new_state(NULL, NULL, 0); + cmdq_add_format(state, "current_file", "%s", pi.file); + + new_item0 = cmdq_get_command(pr->cmdlist, state); if (item != NULL) new_item0 = cmdq_insert_after(item, new_item0); else new_item0 = cmdq_append(NULL, new_item0); cmd_list_free(pr->cmdlist); + cmdq_free_state(state); if (new_item != NULL) *new_item = new_item0; diff --git a/cmd-choose-tree.c b/cmd-choose-tree.c index a58469ac..81209ee3 100644 --- a/cmd-choose-tree.c +++ b/cmd-choose-tree.c @@ -30,9 +30,9 @@ const struct cmd_entry cmd_choose_tree_entry = { .name = "choose-tree", .alias = NULL, - .args = { "F:Gf:NO:rst:wZ", 0, 1 }, - .usage = "[-GNrswZ] [-F format] [-f filter] [-O sort-order] " - CMD_TARGET_PANE_USAGE " [template]", + .args = { "F:f:GK:NO:rst:wZ", 0, 1 }, + .usage = "[-GNrswZ] [-F format] [-f filter] [-K key-format] " + "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -44,9 +44,9 @@ const struct cmd_entry cmd_choose_client_entry = { .name = "choose-client", .alias = NULL, - .args = { "F:f:NO:rt:Z", 0, 1 }, - .usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] " - CMD_TARGET_PANE_USAGE " [template]", + .args = { "F:f:K:NO:rt:Z", 0, 1 }, + .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " + "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, @@ -58,9 +58,9 @@ const struct cmd_entry cmd_choose_buffer_entry = { .name = "choose-buffer", .alias = NULL, - .args = { "F:f:NO:rt:Z", 0, 1 }, - .usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] " - CMD_TARGET_PANE_USAGE " [template]", + .args = { "F:f:K:NO:rt:Z", 0, 1 }, + .usage = "[-NrZ] [-F format] [-f filter] [-K key-format] " + "[-O sort-order] " CMD_TARGET_PANE_USAGE " [template]", .target = { 't', CMD_FIND_PANE, 0 }, diff --git a/cmd-display-message.c b/cmd-display-message.c index fc9c4851..0522d37f 100644 --- a/cmd-display-message.c +++ b/cmd-display-message.c @@ -39,11 +39,11 @@ const struct cmd_entry cmd_display_message_entry = { .name = "display-message", .alias = "display", - .args = { "acd:Ipt:F:v", 0, 1 }, - .usage = "[-aIpv] [-c target-client] [-d delay] [-F format] " + .args = { "acd:INpt:F:v", 0, 1 }, + .usage = "[-aINpv] [-c target-client] [-d delay] [-F format] " CMD_TARGET_PANE_USAGE " [message]", - .target = { 't', CMD_FIND_PANE, 0 }, + .target = { 't', CMD_FIND_PANE, CMD_FIND_CANFAIL }, .flags = CMD_AFTERHOOK|CMD_CLIENT_CFLAG|CMD_CLIENT_CANFAIL, .exec = cmd_display_message_exec @@ -73,6 +73,8 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) int flags; if (args_has(args, 'I')) { + if (wp == NULL) + return (CMD_RETURN_NORMAL); if (window_pane_start_input(wp, item, &cause) != 0) { cmdq_error(item, "%s", cause); free(cause); @@ -109,8 +111,10 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) */ if (tc != NULL && tc->session == s) c = tc; - else + else if (s != NULL) c = cmd_find_best_client(s); + else + c = NULL; if (args_has(args, 'v')) flags = FORMAT_VERBOSE; else @@ -124,10 +128,14 @@ cmd_display_message_exec(struct cmd *self, struct cmdq_item *item) } msg = format_expand_time(ft, template); - if (args_has(args, 'p')) + if (cmdq_get_client(item) == NULL) + cmdq_error(item, "%s", msg); + else if (args_has(args, 'p')) cmdq_print(item, "%s", msg); - else if (tc != NULL) - status_message_set(tc, delay, 0, "%s", msg); + else if (tc != NULL) { + status_message_set(tc, delay, 0, args_has(args, 'N'), "%s", + msg); + } free(msg); format_free(ft); diff --git a/cmd-list-keys.c b/cmd-list-keys.c index dd82e57e..ca4bf752 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -113,9 +113,10 @@ cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, else note = xstrdup(bd->note); tmp = utf8_padcstr(key, keywidth + 1); - if (args_has(args, '1') && tc != NULL) - status_message_set(tc, -1, 1, "%s%s%s", prefix, tmp, note); - else + if (args_has(args, '1') && tc != NULL) { + status_message_set(tc, -1, 1, 0, "%s%s%s", prefix, tmp, + note); + } else cmdq_print(item, "%s%s%s", prefix, tmp, note); free(tmp); free(note); diff --git a/cmd-queue.c b/cmd-queue.c index 05f439f5..54163919 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -276,7 +276,7 @@ cmdq_merge_formats(struct cmdq_item *item, struct format_tree *ft) const struct cmd_entry *entry; if (item->cmd != NULL) { - entry = cmd_get_entry (item->cmd); + entry = cmd_get_entry(item->cmd); format_add(ft, "command", "%s", entry->name); } if (item->state->formats != NULL) @@ -862,7 +862,7 @@ cmdq_error(struct cmdq_item *item, const char *fmt, ...) c->retval = 1; } else { *msg = toupper((u_char) *msg); - status_message_set(c, -1, 1, "%s", msg); + status_message_set(c, -1, 1, 0, "%s", msg); } free(msg); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 4f30d05d..56d5f723 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -20,6 +20,7 @@ #include <sys/types.h> #include <sys/wait.h> +#include <ctype.h> #include <stdlib.h> #include <string.h> @@ -190,8 +191,12 @@ cmd_run_shell_timer(__unused int fd, __unused short events, void* arg) &error); } if (status == CMD_PARSE_ERROR) { - cmdq_error(cdata->item, "%s", error); - free(error); + if (cdata->item == NULL) { + *error = toupper((u_char)*error); + status_message_set(c, -1, 1, 0, "%s", error); + } else + cmdq_error(cdata->item, "%s", error); + free(error); } } diff --git a/control-notify.c b/control-notify.c index cc706ac2..6ff0e436 100644 --- a/control-notify.c +++ b/control-notify.c @@ -171,6 +171,17 @@ control_notify_client_session_changed(struct client *cc) } } +void +control_notify_client_detached(struct client *cc) +{ + struct client *c; + + TAILQ_FOREACH(c, &clients, entry) { + if (CONTROL_SHOULD_NOTIFY_CLIENT(c)) + control_write(c, "%%client-detached %s", cc->name); + } +} + void control_notify_session_renamed(struct session *s) { diff --git a/format.c b/format.c index 0e8ea8f3..5a12502e 100644 --- a/format.c +++ b/format.c @@ -100,6 +100,7 @@ format_job_cmp(struct format_job *fj1, struct format_job *fj2) #define FORMAT_QUOTE_STYLE 0x2000 #define FORMAT_WINDOW_NAME 0x4000 #define FORMAT_SESSION_NAME 0x8000 +#define FORMAT_CHARACTER 0x10000 /* Limit on recursion. */ #define FORMAT_LOOP_LIMIT 10 @@ -3522,7 +3523,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, /* * Modifiers are a ; separated list of the forms: - * l,m,C,b,d,n,t,w,q,E,T,S,W,P,<,> + * l,m,C,a,b,d,n,t,w,q,E,T,S,W,P,<,> * =a * =/a * =/a/ @@ -3539,7 +3540,7 @@ format_build_modifiers(struct format_expand_state *es, const char **s, cp++; /* Check single character modifiers with no arguments. */ - if (strchr("lbdnwETSWP<>", cp[0]) != NULL && + if (strchr("labdnwETSWP<>", cp[0]) != NULL && format_is_end(cp[1])) { format_add_modifier(&list, count, cp, 1, NULL, 0); cp++; @@ -3956,7 +3957,7 @@ format_replace_expression(struct format_modifier *mexp, mright = (long long)mright; } format_log(es, "expression left side is: %.*f", prec, mleft); - format_log(es, "expression right side is: %.*f", prec, mright); + format_log(es, "expression right side is: %.*f", prec, mright); switch (operator) { case ADD: @@ -4016,10 +4017,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, { struct format_tree *ft = es->ft; struct window_pane *wp = ft->wp; - const char *errptr, *copy, *cp, *marker = NULL; + const char *errstr, *copy, *cp, *marker = NULL; const char *time_format = NULL; char *copy0, *condition, *found, *new; - char *value, *left, *right; + char *value, *left, *right, c; size_t valuelen; int modifiers = 0, limit = 0, width = 0; int j; @@ -4063,8 +4064,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, if (fm->argc < 1) break; limit = strtonum(fm->argv[0], INT_MIN, INT_MAX, - &errptr); - if (errptr != NULL) + &errstr); + if (errstr != NULL) limit = 0; if (fm->argc >= 2 && fm->argv[1] != NULL) marker = fm->argv[1]; @@ -4073,8 +4074,8 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, if (fm->argc < 1) break; width = strtonum(fm->argv[0], INT_MIN, INT_MAX, - &errptr); - if (errptr != NULL) + &errstr); + if (errstr != NULL) width = 0; break; case 'w': @@ -4088,6 +4089,9 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'l': modifiers |= FORMAT_LITERAL; break; + case 'a': + modifiers |= FORMAT_CHARACTER; + break; case 'b': modifiers |= FORMAT_BASENAME; break; @@ -4154,6 +4158,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, goto done; } + /* Is this a character? */ + if (modifiers & FORMAT_CHARACTER) { + new = format_expand1(es, copy); + c = strtonum(new, 32, 126, &errstr); + if (errstr != NULL) + value = xstrdup(""); + else + xasprintf(&value, "%c", c); + free (new); + goto done; + } + /* Is this a loop, comparison or condition? */ if (modifiers & FORMAT_SESSIONS) { value = format_loop_sessions(es, copy); diff --git a/grid-reader.c b/grid-reader.c index ae2f4d2b..89fe90fb 100644 --- a/grid-reader.c +++ b/grid-reader.c @@ -71,7 +71,7 @@ grid_reader_cursor_right(struct grid_reader *gr, int wrap, int all) /* Move cursor back one position. */ void -grid_reader_cursor_left(struct grid_reader *gr) +grid_reader_cursor_left(struct grid_reader *gr, int wrap) { struct grid_cell gc; @@ -81,7 +81,9 @@ grid_reader_cursor_left(struct grid_reader *gr) break; gr->cx--; } - if (gr->cx == 0 && gr->cy > 0) { + if (gr->cx == 0 && gr->cy > 0 && + (wrap || + grid_get_line(gr->gd, gr->cy - 1)->flags & GRID_LINE_WRAPPED)) { grid_reader_cursor_up(gr); grid_reader_cursor_end_of_line(gr, 0, 0); } else if (gr->cx > 0) @@ -363,3 +365,25 @@ grid_reader_cursor_jump_back(struct grid_reader *gr, const struct utf8_data *jc) } return 0; } + +/* Jump back to the first non-blank character of the line. */ +void +grid_reader_cursor_back_to_indentation(struct grid_reader *gr) +{ + struct grid_cell gc; + u_int px, py, xx, yy; + + yy = gr->gd->hsize + gr->gd->sy - 1; + grid_reader_cursor_start_of_line(gr, 1); + + for (py = gr->cy; py <= yy; py++) { + xx = grid_line_length(gr->gd, py); + for (px = 0; px < xx; px++) { + grid_get_cell(gr->gd, px, py, &gc); + if (gc.data.size != 1 || *gc.data.data != ' ') + break; + } + if (~grid_get_line(gr->gd, py)->flags & GRID_LINE_WRAPPED) + break; + } +} diff --git a/input-keys.c b/input-keys.c index 39a72cdc..a3252855 100644 --- a/input-keys.c +++ b/input-keys.c @@ -428,6 +428,14 @@ input_key_pane(struct window_pane *wp, key_code key, struct mouse_event *m) return (input_key(wp->screen, wp->event, key)); } +static void +input_key_write(const char *from, struct bufferevent *bev, const char *data, + size_t size) +{ + log_debug("%s: %.*s", from, (int)size, data); + bufferevent_write(bev, data, size); +} + /* Translate a key code into an output key sequence. */ int input_key(struct screen *s, struct bufferevent *bev, key_code key) @@ -444,7 +452,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) /* Literal keys go as themselves (can't be more than eight bits). */ if (key & KEYC_LITERAL) { ud.data[0] = (u_char)key; - bufferevent_write(bev, &ud.data[0], 1); + input_key_write(__func__, bev, &ud.data[0], 1); return (0); } @@ -463,16 +471,16 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) justkey = (key & ~(KEYC_META|KEYC_IMPLIED_META)); if (justkey <= 0x7f) { if (key & KEYC_META) - bufferevent_write(bev, "\033", 1); + input_key_write(__func__, bev, "\033", 1); ud.data[0] = justkey; - bufferevent_write(bev, &ud.data[0], 1); + input_key_write(__func__, bev, &ud.data[0], 1); return (0); } if (justkey > 0x7f && justkey < KEYC_BASE) { if (key & KEYC_META) - bufferevent_write(bev, "\033", 1); + input_key_write(__func__, bev, "\033", 1); utf8_to_data(justkey, &ud); - bufferevent_write(bev, ud.data, ud.size); + input_key_write(__func__, bev, ud.data, ud.size); return (0); } @@ -494,8 +502,8 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) if (ike != NULL) { log_debug("found key 0x%llx: \"%s\"", key, ike->data); if ((key & KEYC_META) && (~key & KEYC_IMPLIED_META)) - bufferevent_write(bev, "\033", 1); - bufferevent_write(bev, ike->data, strlen(ike->data)); + input_key_write(__func__, bev, "\033", 1); + input_key_write(__func__, bev, ike->data, strlen(ike->data)); return (0); } @@ -560,7 +568,7 @@ input_key(struct screen *s, struct bufferevent *bev, key_code key) goto missing; } xsnprintf(tmp, sizeof tmp, "\033[%llu;%cu", outkey, modifier); - bufferevent_write(bev, tmp, strlen(tmp)); + input_key_write(__func__, bev, tmp, strlen(tmp)); return (0); missing: @@ -656,5 +664,5 @@ input_key_mouse(struct window_pane *wp, struct mouse_event *m) if (!input_key_get_mouse(s, m, x, y, &buf, &len)) return; log_debug("writing mouse %.*s to %%%u", (int)len, buf, wp->id); - bufferevent_write(wp->event, buf, len); + input_key_write(__func__, wp->event, buf, len); } diff --git a/key-string.c b/key-string.c index 194fdef2..8d60f132 100644 --- a/key-string.c +++ b/key-string.c @@ -45,9 +45,9 @@ static const struct { { "F11", KEYC_F11|KEYC_IMPLIED_META }, { "F12", KEYC_F12|KEYC_IMPLIED_META }, { "IC", KEYC_IC|KEYC_IMPLIED_META }, - { "Insert", KEYC_IC|KEYC_IMPLIED_META }, + { "Insert", KEYC_IC|KEYC_IMPLIED_META }, { "DC", KEYC_DC|KEYC_IMPLIED_META }, - { "Delete", KEYC_DC|KEYC_IMPLIED_META }, + { "Delete", KEYC_DC|KEYC_IMPLIED_META }, { "Home", KEYC_HOME|KEYC_IMPLIED_META }, { "End", KEYC_END|KEYC_IMPLIED_META }, { "NPage", KEYC_NPAGE|KEYC_IMPLIED_META }, @@ -70,7 +70,7 @@ static const struct { { "Right", KEYC_RIGHT|KEYC_CURSOR|KEYC_IMPLIED_META }, /* Numeric keypad. */ - { "KP/", KEYC_KP_SLASH|KEYC_KEYPAD }, + { "KP/", KEYC_KP_SLASH|KEYC_KEYPAD }, { "KP*", KEYC_KP_STAR|KEYC_KEYPAD }, { "KP-", KEYC_KP_MINUS|KEYC_KEYPAD }, { "KP7", KEYC_KP_SEVEN|KEYC_KEYPAD }, @@ -164,7 +164,7 @@ key_string_get_modifiers(const char **string) key_code key_string_lookup_string(const char *string) { - static const char *other = "!#()+,-.0123456789:;<=>'\r\t"; + static const char *other = "!#()+,-.0123456789:;<=>'\r\t\177"; key_code key, modifiers; u_int u, i; struct utf8_data ud, *udp; @@ -181,8 +181,8 @@ key_string_lookup_string(const char *string) /* Is this a hexadecimal value? */ if (string[0] == '0' && string[1] == 'x') { - if (sscanf(string + 2, "%x", &u) != 1) - return (KEYC_UNKNOWN); + if (sscanf(string + 2, "%x", &u) != 1) + return (KEYC_UNKNOWN); mlen = wctomb(m, u); if (mlen <= 0 || mlen > MB_LEN_MAX) return (KEYC_UNKNOWN); @@ -238,11 +238,12 @@ key_string_lookup_string(const char *string) } /* Convert the standard control keys. */ - if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && !strchr(other, key)) { + if (key < KEYC_BASE && (modifiers & KEYC_CTRL) && + strchr(other, key) == NULL) { if (key >= 97 && key <= 122) key -= 96; else if (key >= 64 && key <= 95) - key -= 64; + key -= 64; else if (key == 32) key = 0; else if (key == 63) diff --git a/log.c b/log.c index f87cab92..26569974 100644 --- a/log.c +++ b/log.c @@ -110,15 +110,16 @@ log_vwrite(const char *msg, va_list ap) return; if (vasprintf(&fmt, msg, ap) == -1) - exit(1); - if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) - exit(1); + return; + if (stravis(&out, fmt, VIS_OCTAL|VIS_CSTYLE|VIS_TAB|VIS_NL) == -1) { + free(fmt); + return; + } gettimeofday(&tv, NULL); if (fprintf(log_file, "%lld.%06d %s\n", (long long)tv.tv_sec, - (int)tv.tv_usec, out) == -1) - exit(1); - fflush(log_file); + (int)tv.tv_usec, out) != -1) + fflush(log_file); free(out); free(fmt); diff --git a/mode-tree.c b/mode-tree.c index a47c0c06..ca7f33a4 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -46,6 +46,7 @@ struct mode_tree_data { mode_tree_search_cb searchcb; mode_tree_menu_cb menucb; mode_tree_height_cb heightcb; + mode_tree_key_cb keycb; struct mode_tree_list children; struct mode_tree_list saved; @@ -74,6 +75,10 @@ struct mode_tree_item { void *itemdata; u_int line; + key_code key; + const char *keystr; + size_t keylen; + uint64_t tag; const char *name; const char *text; @@ -135,6 +140,7 @@ mode_tree_free_item(struct mode_tree_item *mti) free((void *)mti->name); free((void *)mti->text); + free((void *)mti->keystr); free(mti); } @@ -193,6 +199,26 @@ mode_tree_build_lines(struct mode_tree_data *mtd, flat = 0; if (mti->expanded) mode_tree_build_lines(mtd, &mti->children, depth + 1); + + if (mtd->keycb != NULL) { + mti->key = mtd->keycb(mtd->modedata, mti->itemdata, + mti->line); + if (mti->key == KEYC_UNKNOWN) + mti->key = KEYC_NONE; + } else if (mti->line < 10) + mti->key = '0' + mti->line; + else if (mti->line < 36) + mti->key = KEYC_META|('a' + mti->line - 10); + else + mti->key = KEYC_NONE; + if (mti->key != KEYC_NONE) { + mti->keystr = xstrdup(key_string_lookup_key(mti->key, + 0)); + mti->keylen = strlen(mti->keystr); + } else { + mti->keystr = NULL; + mti->keylen = 0; + } } TAILQ_FOREACH(mti, mtl, entry) { for (i = 0; i < mtd->line_size; i++) { @@ -363,7 +389,7 @@ struct mode_tree_data * mode_tree_start(struct window_pane *wp, struct args *args, 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, void *modedata, + mode_tree_height_cb heightcb, mode_tree_key_cb keycb, void *modedata, const struct menu_item *menu, const char **sort_list, u_int sort_size, struct screen **s) { @@ -402,6 +428,7 @@ mode_tree_start(struct window_pane *wp, struct args *args, mtd->searchcb = searchcb; mtd->menucb = menucb; mtd->heightcb = heightcb; + mtd->keycb = keycb; TAILQ_INIT(&mtd->children); @@ -596,10 +623,10 @@ mode_tree_draw(struct mode_tree_data *mtd) struct screen_write_ctx ctx; struct grid_cell gc0, gc; u_int w, h, i, j, sy, box_x, box_y, width; - char *text, *start, key[7]; + char *text, *start, *key; const char *tag, *symbol; size_t size, n; - int keylen; + int keylen, pad; if (mtd->line_size == 0) return; @@ -614,28 +641,30 @@ mode_tree_draw(struct mode_tree_data *mtd) screen_write_start(&ctx, s); screen_write_clearscreen(&ctx, 8); - if (mtd->line_size > 10) - keylen = 6; - else - keylen = 4; + keylen = 0; + for (i = 0; i < mtd->line_size; i++) { + mti = mtd->line_list[i].item; + if (mti->key == KEYC_NONE) + continue; + if ((int)mti->keylen + 3 > keylen) + keylen = mti->keylen + 3; + } for (i = 0; i < mtd->line_size; i++) { if (i < mtd->offset) continue; if (i > mtd->offset + h - 1) break; - line = &mtd->line_list[i]; mti = line->item; screen_write_cursormove(&ctx, 0, i - mtd->offset, 0); - if (i < 10) - snprintf(key, sizeof key, "(%c) ", '0' + i); - else if (i < 36) - snprintf(key, sizeof key, "(M-%c)", 'a' + (i - 10)); + pad = keylen - 2 - mti->keylen; + if (mti->key != KEYC_NONE) + xasprintf(&key, "(%s)%*s", mti->keystr, pad, ""); else - *key = '\0'; + key = xstrdup(""); if (line->flat) symbol = ""; @@ -698,6 +727,7 @@ mode_tree_draw(struct mode_tree_data *mtd) } } free(text); + free(key); if (mti->tagged) { gc.attr ^= GRID_ATTR_BRIGHT; @@ -951,7 +981,6 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, struct mode_tree_item *current, *parent, *mti; u_int i, x, y; int choice; - key_code tmp; if (KEYC_IS_MOUSE(*key) && m != NULL) { if (cmd_mouse_at(mtd->wp, m, &x, &y, 0) != 0) { @@ -993,12 +1022,11 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, current = line->item; choice = -1; - if (*key >= '0' && *key <= '9') - choice = (*key) - '0'; - else if (((*key) & KEYC_MASK_MODIFIERS) == KEYC_META) { - tmp = (*key) & KEYC_MASK_KEY; - if (tmp >= 'a' && tmp <= 'z') - choice = 10 + (tmp - 'a'); + for (i = 0; i < mtd->line_size; i++) { + if (*key == mtd->line_list[i].item->key) { + choice = i; + break; + } } if (choice != -1) { if ((u_int)choice > mtd->line_size - 1) { @@ -1176,7 +1204,7 @@ mode_tree_run_command(struct client *c, struct cmd_find_state *fs, if (status == CMD_PARSE_ERROR) { if (c != NULL) { *error = toupper((u_char)*error); - status_message_set(c, -1, 1, "%s", error); + status_message_set(c, -1, 1, 0, "%s", error); } free(error); } diff --git a/notify.c b/notify.c index f7440cad..4b9b1eaa 100644 --- a/notify.c +++ b/notify.c @@ -125,6 +125,8 @@ notify_callback(struct cmdq_item *item, void *data) control_notify_window_renamed(ne->window); if (strcmp(ne->name, "client-session-changed") == 0) control_notify_client_session_changed(ne->client); + if (strcmp(ne->name, "client-detached") == 0) + control_notify_client_detached(ne->client); if (strcmp(ne->name, "session-renamed") == 0) control_notify_session_renamed(ne->session); if (strcmp(ne->name, "session-created") == 0) diff --git a/screen.c b/screen.c index db2590cc..2b9d70de 100644 --- a/screen.c +++ b/screen.c @@ -153,8 +153,10 @@ screen_reset_tabs(struct screen *s) void screen_set_cursor_style(struct screen *s, u_int style) { - if (style <= 6) + if (style <= 6) { s->cstyle = style; + s->mode &= ~MODE_BLINKING; + } } /* Set screen cursor colour. */ diff --git a/server-client.c b/server-client.c index 96e1b584..d3ffd682 100644 --- a/server-client.c +++ b/server-client.c @@ -296,6 +296,9 @@ server_client_lost(struct client *c) TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); + if (c->flags & CLIENT_ATTACHED) + notify_client("client-detached", c); + if (c->flags & CLIENT_CONTROL) control_stop(c); if (c->flags & CLIENT_TERMINAL) @@ -1305,7 +1308,11 @@ server_client_handle_key(struct client *c, struct key_event *event) * immediately rather than queued. */ if (~c->flags & CLIENT_READONLY) { - status_message_clear(c); + if (c->message_string != NULL) { + if (c->message_ignore_keys) + return (0); + status_message_clear(c); + } if (c->overlay_key != NULL) { switch (c->overlay_key(c, event)) { case 0: @@ -1766,9 +1773,6 @@ server_client_check_exit(struct client *c) if (EVBUFFER_LENGTH(cf->buffer) != 0) return; } - - if (c->flags & CLIENT_ATTACHED) - notify_client("client-detached", c); c->flags |= CLIENT_EXITED; switch (c->exit_type) { diff --git a/status.c b/status.c index 154d9452..f9786f4b 100644 --- a/status.c +++ b/status.c @@ -424,7 +424,7 @@ status_redraw(struct client *c) /* Set a status line message. */ void status_message_set(struct client *c, int delay, int ignore_styles, - const char *fmt, ...) + int ignore_keys, const char *fmt, ...) { struct timeval tv; va_list ap; @@ -433,7 +433,6 @@ status_message_set(struct client *c, int delay, int ignore_styles, status_push_screen(c); va_start(ap, fmt); - c->message_ignore_styles = ignore_styles; xvasprintf(&c->message_string, fmt, ap); va_end(ap); @@ -456,6 +455,10 @@ status_message_set(struct client *c, int delay, int ignore_styles, evtimer_add(&c->message_timer, &tv); } + if (delay != 0) + c->message_ignore_keys = ignore_keys; + c->message_ignore_styles = ignore_styles; + c->tty.flags |= (TTY_NOCURSOR|TTY_FREEZE); c->flags |= CLIENT_REDRAWSTATUS; } diff --git a/tmux.1 b/tmux.1 index c79950ce..2343ab89 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1985,12 +1985,17 @@ The default is to capture only the visible contents of the pane. .Op Fl NrZ .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl K Ar key-format .Op Fl O Ar sort-order .Op Fl t Ar target-pane .Op Ar template .Xc Put a pane into client mode, allowing a client to be selected interactively from a list. +Each client is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the list may be navigated and an item chosen or otherwise manipulated using +the keys below. .Fl Z zooms the pane. The following keys may be used in client mode: @@ -2040,7 +2045,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. If a filter would lead to an empty list, it is ignored. .Fl F -specifies the format for each item in the list. +specifies the format for each item in the list and +.Fl K +a format for each shortcut key; both are evaluated once for each line. .Fl N starts without the preview. This command works only if at least one client is attached. @@ -2049,12 +2056,17 @@ This command works only if at least one client is attached. .Op Fl GNrswZ .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl K Ar key-format .Op Fl O Ar sort-order .Op Fl t Ar target-pane .Op Ar template .Xc Put a pane into tree mode, where a session, window or pane may be chosen -interactively from a list. +interactively from a tree. +Each session, window or pane is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the tree may be navigated and an item chosen or otherwise manipulated using +the keys below. .Fl s starts with sessions collapsed and .Fl w @@ -2113,7 +2125,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. If a filter would lead to an empty list, it is ignored. .Fl F -specifies the format for each item in the tree. +specifies the format for each item in the tree and +.Fl K +a format for each shortcut key; both are evaluated once for each line. .Fl N starts without the preview. .Fl G @@ -3149,8 +3163,8 @@ The appearance and behaviour of may be modified by changing the value of various options. There are four types of option: .Em server options , -.Em session options -.Em window options +.Em session options , +.Em window options , and .Em pane options . .Pp @@ -4663,6 +4677,11 @@ For example, multiplies 5.5 by 3 for a result with four decimal places and .Ql #{e|%%:7,3} returns the modulus of 7 and 3. +.Ql a +replaces a numeric argument by its ASCII equivalent, so +.Ql #{a:98} +results in +.Ql b . .Pp A limit may be placed on the length of the resultant string by prefixing it by an @@ -4859,6 +4878,7 @@ The following variables are available, where appropriate: .It Li "copy_cursor_word" Ta "" Ta "Word under cursor in copy mode" .It Li "copy_cursor_x" Ta "" Ta "Cursor X position in copy mode" .It Li "copy_cursor_y" Ta "" Ta "Cursor Y position in copy mode" +.It Li "current_file" Ta "" Ta "Current configuration file" .It Li "cursor_character" Ta "" Ta "Character at cursor in pane" .It Li "cursor_flag" Ta "" Ta "Pane cursor flag" .It Li "cursor_x" Ta "" Ta "Cursor X position in pane" @@ -5545,7 +5565,7 @@ The following keys are also available: .It Li "q" Ta "Exit menu" .El .It Xo Ic display-message -.Op Fl aIpv +.Op Fl aINpv .Op Fl c Ar target-client .Op Fl d Ar delay .Op Fl t Ar target-pane @@ -5565,6 +5585,8 @@ If is not given, the .Ic message-time option is used; a delay of zero waits for a key press. +.Ql N +ignores key presses and closes only after the delay expires. The format of .Ar message is described in the @@ -5680,12 +5702,17 @@ The buffer commands are as follows: .Op Fl NZr .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl K Ar key-format .Op Fl O Ar sort-order .Op Fl t Ar target-pane .Op Ar template .Xc Put a pane into buffer mode, where a buffer may be chosen interactively from a list. +Each buffer is shown on one line. +A shortcut key is shown on the left in brackets allowing for immediate choice, +or the list may be navigated and an item chosen or otherwise manipulated using +the keys below. .Fl Z zooms the pane. The following keys may be used in buffer mode: @@ -5733,7 +5760,9 @@ specifies an initial filter: the filter is a format - if it evaluates to zero, the item in the list is not shown, otherwise it is shown. If a filter would lead to an empty list, it is ignored. .Fl F -specifies the format for each item in the list. +specifies the format for each item in the list and +.Fl K +a format for each shortcut key; both are evaluated once for each line. .Fl N starts without the preview. This command works only if at least one client is attached. @@ -6107,6 +6136,8 @@ A notification will never occur inside an output block. .Pp The following notifications are defined: .Bl -tag -width Ds +.It Ic %client-detached Ar client +The client has detached. .It Ic %client-session-changed Ar client session-id name The client is now attached to the session with ID .Ar session-id , diff --git a/tmux.h b/tmux.h index ca71559f..6be5b302 100644 --- a/tmux.h +++ b/tmux.h @@ -1688,6 +1688,7 @@ struct client { uint64_t redraw_panes; + int message_ignore_keys; int message_ignore_styles; char *message_string; struct event message_timer; @@ -2491,7 +2492,7 @@ struct style_range *status_get_range(struct client *, u_int, u_int); void status_init(struct client *); void status_free(struct client *); int status_redraw(struct client *); -void status_message_set(struct client *, int, int, const char *, ...); +void status_message_set(struct client *, int, int, int, const char *, ...); void status_message_clear(struct client *); int status_message_redraw(struct client *); void status_prompt_set(struct client *, struct cmd_find_state *, @@ -2584,7 +2585,7 @@ void grid_reader_get_cursor(struct grid_reader *, u_int *, u_int *); u_int grid_reader_line_length(struct grid_reader *); int grid_reader_in_set(struct grid_reader *, const char *); void grid_reader_cursor_right(struct grid_reader *, int, int); -void grid_reader_cursor_left(struct grid_reader *); +void grid_reader_cursor_left(struct grid_reader *, int); void grid_reader_cursor_down(struct grid_reader *); void grid_reader_cursor_up(struct grid_reader *); void grid_reader_cursor_start_of_line(struct grid_reader *, int); @@ -2597,6 +2598,7 @@ int grid_reader_cursor_jump(struct grid_reader *, const struct utf8_data *); int grid_reader_cursor_jump_back(struct grid_reader *, const struct utf8_data *); +void grid_reader_cursor_back_to_indentation(struct grid_reader *); /* grid-view.c */ void grid_view_get_cell(struct grid *, u_int, u_int, struct grid_cell *); @@ -2855,6 +2857,7 @@ typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *, typedef int (*mode_tree_search_cb)(void *, void *, const char *); typedef void (*mode_tree_menu_cb)(void *, struct client *, key_code); typedef u_int (*mode_tree_height_cb)(void *, u_int); +typedef key_code (*mode_tree_key_cb)(void *, void *, u_int); typedef void (*mode_tree_each_cb)(void *, void *, struct client *, key_code); u_int mode_tree_count_tagged(struct mode_tree_data *); void *mode_tree_get_current(struct mode_tree_data *); @@ -2869,7 +2872,7 @@ void mode_tree_up(struct mode_tree_data *, int); void mode_tree_down(struct mode_tree_data *, int); struct mode_tree_data *mode_tree_start(struct window_pane *, struct args *, mode_tree_build_cb, mode_tree_draw_cb, mode_tree_search_cb, - mode_tree_menu_cb, mode_tree_height_cb, void *, + mode_tree_menu_cb, mode_tree_height_cb, mode_tree_key_cb, void *, const struct menu_item *, const char **, u_int, struct screen **); void mode_tree_zoom(struct mode_tree_data *, struct args *); void mode_tree_build(struct mode_tree_data *); @@ -2946,6 +2949,7 @@ void control_notify_window_unlinked(struct session *, struct window *); void control_notify_window_linked(struct session *, struct window *); void control_notify_window_renamed(struct window *); void control_notify_client_session_changed(struct client *); +void control_notify_client_detached(struct client *); void control_notify_session_renamed(struct session *); void control_notify_session_created(struct session *); void control_notify_session_closed(struct session *); diff --git a/tty-keys.c b/tty-keys.c index 59426772..e88ff227 100644 --- a/tty-keys.c +++ b/tty-keys.c @@ -61,7 +61,7 @@ static int tty_keys_extended_device_attributes(struct tty *, const char *, /* Default raw keys. */ struct tty_default_key_raw { const char *string; - key_code key; + key_code key; }; static const struct tty_default_key_raw tty_default_raw_keys[] = { /* Application escape. */ @@ -262,7 +262,7 @@ static const key_code tty_default_xterm_modifiers[] = { */ struct tty_default_key_code { enum tty_code_code code; - key_code key; + key_code key; }; static const struct tty_default_key_code tty_default_code_keys[] = { /* Function keys. */ @@ -420,7 +420,7 @@ tty_keys_add(struct tty *tty, const char *s, key_code key) { struct tty_key *tk; size_t size; - const char *keystr; + const char *keystr; keystr = key_string_lookup_key(key, 1); if ((tk = tty_keys_find(tty, s, strlen(s), &size)) == NULL) { @@ -477,7 +477,7 @@ tty_keys_build(struct tty *tty) const struct tty_default_key_raw *tdkr; const struct tty_default_key_xterm *tdkx; const struct tty_default_key_code *tdkc; - u_int i, j; + u_int i, j; const char *s; struct options_entry *o; struct options_array_item *a; @@ -869,6 +869,9 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, size_t end; u_int number, modifiers; char tmp[64]; + cc_t bspace; + key_code nkey; + key_code onlykey; *size = 0; @@ -911,38 +914,68 @@ tty_keys_extended_key(struct tty *tty, const char *buf, size_t len, } *size = end + 1; - /* Store the key and modifiers. */ - *key = number; + /* Store the key. */ + bspace = tty->tio.c_cc[VERASE]; + if (bspace != _POSIX_VDISABLE && number == bspace) + nkey = KEYC_BSPACE; + else + nkey = number; + + /* Update the modifiers. */ switch (modifiers) { case 2: - (*key) |= KEYC_SHIFT; + nkey |= KEYC_SHIFT; break; case 3: - (*key) |= (KEYC_META|KEYC_IMPLIED_META); + nkey |= (KEYC_META|KEYC_IMPLIED_META); break; case 4: - (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META); + nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META); break; case 5: - (*key) |= KEYC_CTRL; + nkey |= KEYC_CTRL; break; case 6: - (*key) |= (KEYC_SHIFT|KEYC_CTRL); + nkey |= (KEYC_SHIFT|KEYC_CTRL); break; case 7: - (*key) |= (KEYC_META|KEYC_CTRL); + nkey |= (KEYC_META|KEYC_CTRL); break; case 8: - (*key) |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); + nkey |= (KEYC_SHIFT|KEYC_META|KEYC_IMPLIED_META|KEYC_CTRL); break; default: *key = KEYC_NONE; break; } + + /* + * Don't allow both KEYC_CTRL and as an implied modifier. Also convert + * C-X into C-x and so on. + */ + if (nkey & KEYC_CTRL){ + onlykey = (nkey & KEYC_MASK_KEY); + if (onlykey < 32) + onlykey = (nkey & ~KEYC_CTRL); + else { + if (onlykey >= 97 && onlykey <= 122) + onlykey -= 96; + else if (onlykey >= 64 && onlykey <= 95) + onlykey -= 64; + else if (onlykey == 32) + onlykey = 0; + else if (onlykey == 63) + onlykey = 127; + onlykey |= ((nkey & KEYC_MASK_MODIFIERS) & ~KEYC_CTRL); + } + nkey = onlykey; + } + if (log_get_level() != 0) { log_debug("%s: extended key %.*s is %llx (%s)", c->name, - (int)*size, buf, *key, key_string_lookup_key(*key, 1)); + (int)*size, buf, nkey, key_string_lookup_key(nkey, 1)); } + *key = nkey; return (0); } diff --git a/tty.c b/tty.c index 399bbae8..e8a8cbaa 100644 --- a/tty.c +++ b/tty.c @@ -670,19 +670,10 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) if (changed != 0) log_debug("%s: update mode %x to %x", c->name, tty->mode, mode); - if (changed & MODE_BLINKING) { - if (tty_term_has(tty->term, TTYC_CVVIS)) - tty_putcode(tty, TTYC_CVVIS); - else - tty_putcode(tty, TTYC_CNORM); - changed |= MODE_CURSOR; - } - if (changed & MODE_CURSOR) { - if (mode & MODE_CURSOR) - tty_putcode(tty, TTYC_CNORM); - else - tty_putcode(tty, TTYC_CIVIS); - } + /* + * The cursor blinking flag can be reset by setting the cursor style, so + * set the style first. + */ if (s != NULL && tty->cstyle != s->cstyle) { if (tty_term_has(tty->term, TTYC_SS)) { if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) @@ -691,7 +682,28 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) tty_putcode1(tty, TTYC_SS, s->cstyle); } tty->cstyle = s->cstyle; + changed |= (MODE_CURSOR|MODE_BLINKING); } + + /* + * Cursor invisible (RM ?25) overrides cursor blinking (SM ?12 or RM + * 34), and we need to be careful not send cnorm after cvvis since it + * can undo it. + */ + if (changed & (MODE_CURSOR|MODE_BLINKING)) { + log_debug("%s: cursor %s, %sblinking", __func__, + (mode & MODE_CURSOR) ? "on" : "off", + (mode & MODE_BLINKING) ? "" : "not "); + if (~mode & MODE_CURSOR) + tty_putcode(tty, TTYC_CIVIS); + else if (mode & MODE_BLINKING) { + tty_putcode(tty, TTYC_CNORM); + if (tty_term_has(tty->term, TTYC_CVVIS)) + tty_putcode(tty, TTYC_CVVIS); + } else + tty_putcode(tty, TTYC_CNORM); + } + if ((changed & ALL_MOUSE_MODES) && tty_term_has(tty->term, TTYC_KMOUS)) { /* diff --git a/window-buffer.c b/window-buffer.c index acdcb525..8d93558a 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -41,6 +41,17 @@ static void window_buffer_key(struct window_mode_entry *, #define WINDOW_BUFFER_DEFAULT_FORMAT \ "#{t/p:buffer_created}: #{buffer_sample}" +#define WINDOW_BUFFER_DEFAULT_KEY_FORMAT \ + "#{?#{e|<:#{line},10}," \ + "#{line}" \ + "," \ + "#{?#{e|<:#{line},36}," \ + "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ + "," \ + "" \ + "}" \ + "}" + static const struct menu_item window_buffer_menu_items[] = { { "Paste", 'p', NULL }, { "Paste Tagged", 'P', NULL }, @@ -93,6 +104,7 @@ struct window_buffer_modedata { struct mode_tree_data *data; char *command; char *format; + char *key_format; struct window_buffer_itemdata **item_list; u_int item_size; @@ -232,7 +244,8 @@ window_buffer_draw(__unused void *modedata, void *itemdata, while (end != pdata + psize && *end != '\n') end++; buf = xreallocarray(buf, 4, end - start + 1); - utf8_strvis(buf, start, end - start, VIS_OCTAL|VIS_CSTYLE|VIS_TAB); + utf8_strvis(buf, start, end - start, + VIS_OCTAL|VIS_CSTYLE|VIS_TAB); if (*buf != '\0') { screen_write_cursormove(ctx, cx, cy + i, 0); screen_write_nputs(ctx, sx, &grid_default_cell, "%s", @@ -275,6 +288,41 @@ window_buffer_menu(void *modedata, struct client *c, key_code key) window_buffer_key(wme, c, NULL, NULL, key, NULL); } +static key_code +window_buffer_get_key(void *modedata, void *itemdata, u_int line) +{ + struct window_buffer_modedata *data = modedata; + struct window_buffer_itemdata *item = itemdata; + struct format_tree *ft; + struct session *s; + struct winlink *wl; + struct window_pane *wp; + struct paste_buffer *pb; + char *expanded; + key_code key; + + if (cmd_find_valid_state(&data->fs)) { + s = data->fs.s; + wl = data->fs.wl; + wp = data->fs.wp; + } + pb = paste_get_name(item->name); + if (pb == NULL) + return KEYC_NONE; + + ft = format_create(NULL, NULL, FORMAT_NONE, 0); + format_defaults(ft, NULL, NULL, 0, NULL); + format_defaults(ft, NULL, s, wl, wp); + format_defaults_paste_buffer(ft, pb); + format_add(ft, "line", "%u", line); + + expanded = format_expand(ft, data->key_format); + key = key_string_lookup_string(expanded); + free(expanded); + format_free(ft); + return key; +} + static struct screen * window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) @@ -291,6 +339,10 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->format = xstrdup(WINDOW_BUFFER_DEFAULT_FORMAT); else data->format = xstrdup(args_get(args, 'F')); + if (args == NULL || !args_has(args, 'K')) + data->key_format = xstrdup(WINDOW_BUFFER_DEFAULT_KEY_FORMAT); + else + data->key_format = xstrdup(args_get(args, 'K')); if (args == NULL || args->argc == 0) data->command = xstrdup(WINDOW_BUFFER_DEFAULT_COMMAND); else @@ -298,8 +350,8 @@ window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->data = mode_tree_start(wp, args, window_buffer_build, window_buffer_draw, window_buffer_search, window_buffer_menu, NULL, - data, window_buffer_menu_items, window_buffer_sort_list, - nitems(window_buffer_sort_list), &s); + window_buffer_get_key, data, window_buffer_menu_items, + window_buffer_sort_list, nitems(window_buffer_sort_list), &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); @@ -324,6 +376,7 @@ window_buffer_free(struct window_mode_entry *wme) free(data->item_list); free(data->format); + free(data->key_format); free(data->command); free(data); diff --git a/window-client.c b/window-client.c index ec3c646a..db7c6dcc 100644 --- a/window-client.c +++ b/window-client.c @@ -40,6 +40,17 @@ static void window_client_key(struct window_mode_entry *, #define WINDOW_CLIENT_DEFAULT_FORMAT \ "#{t/p:client_activity}: session #{session_name}" +#define WINDOW_CLIENT_DEFAULT_KEY_FORMAT \ + "#{?#{e|<:#{line},10}," \ + "#{line}" \ + "," \ + "#{?#{e|<:#{line},36}," \ + "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ + "," \ + "" \ + "}" \ + "}" + static const struct menu_item window_client_menu_items[] = { { "Detach", 'd', NULL }, { "Detach Tagged", 'D', NULL }, @@ -87,6 +98,7 @@ struct window_client_modedata { struct mode_tree_data *data; char *format; + char *key_format; char *command; struct window_client_itemdata **item_list; @@ -252,6 +264,26 @@ window_client_menu(void *modedata, struct client *c, key_code key) window_client_key(wme, c, NULL, NULL, key, NULL); } +static key_code +window_client_get_key(void *modedata, void *itemdata, u_int line) +{ + struct window_client_modedata *data = modedata; + struct window_client_itemdata *item = itemdata; + struct format_tree *ft; + char *expanded; + key_code key; + + ft = format_create(NULL, NULL, FORMAT_NONE, 0); + format_defaults(ft, item->c, NULL, 0, NULL); + format_add(ft, "line", "%u", line); + + expanded = format_expand(ft, data->key_format); + key = key_string_lookup_string(expanded); + free(expanded); + format_free(ft); + return key; +} + static struct screen * window_client_init(struct window_mode_entry *wme, __unused struct cmd_find_state *fs, struct args *args) @@ -267,15 +299,19 @@ window_client_init(struct window_mode_entry *wme, data->format = xstrdup(WINDOW_CLIENT_DEFAULT_FORMAT); else data->format = xstrdup(args_get(args, 'F')); + if (args == NULL || !args_has(args, 'K')) + data->key_format = xstrdup(WINDOW_CLIENT_DEFAULT_KEY_FORMAT); + else + data->key_format = xstrdup(args_get(args, 'K')); if (args == NULL || args->argc == 0) data->command = xstrdup(WINDOW_CLIENT_DEFAULT_COMMAND); else data->command = xstrdup(args->argv[0]); data->data = mode_tree_start(wp, args, window_client_build, - window_client_draw, NULL, window_client_menu, NULL, data, - window_client_menu_items, window_client_sort_list, - nitems(window_client_sort_list), &s); + window_client_draw, NULL, window_client_menu, NULL, + window_client_get_key, data, window_client_menu_items, + window_client_sort_list, nitems(window_client_sort_list), &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); @@ -300,6 +336,7 @@ window_client_free(struct window_mode_entry *wme) free(data->item_list); free(data->format); + free(data->key_format); free(data->command); free(data); diff --git a/window-copy.c b/window-copy.c index 3fc7ad3e..423cce8f 100644 --- a/window-copy.c +++ b/window-copy.c @@ -64,6 +64,8 @@ static int window_copy_search_rl(struct grid *, struct grid *, u_int *, static int window_copy_last_regex(struct grid *, u_int, u_int, u_int, u_int, u_int *, u_int *, const char *, const regex_t *, int); +static int window_copy_search_mark_at(struct window_copy_mode_data *, + u_int, u_int, u_int *); static char *window_copy_stringify(struct grid *, u_int, u_int, u_int, char *, u_int *); static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, @@ -71,16 +73,15 @@ static void window_copy_cstrtocellpos(struct grid *, u_int, u_int *, static int window_copy_search_marks(struct window_mode_entry *, struct screen *, int, int); static void window_copy_clear_marks(struct window_mode_entry *); -static void window_copy_move_left(struct screen *, u_int *, u_int *, int); static int window_copy_is_lowercase(const char *); static void window_copy_search_back_overlap(struct grid *, regex_t *, u_int *, u_int *, u_int *, u_int); static int window_copy_search_jump(struct window_mode_entry *, struct grid *, struct grid *, u_int, u_int, u_int, int, int, - int, int, u_int *); -static int window_copy_search(struct window_mode_entry *, int, int, int); -static int window_copy_search_up(struct window_mode_entry *, int, int); -static int window_copy_search_down(struct window_mode_entry *, int, int); + int, int); +static int window_copy_search(struct window_mode_entry *, int, int); +static int window_copy_search_up(struct window_mode_entry *, int); +static int window_copy_search_down(struct window_mode_entry *, int); static void window_copy_goto_line(struct window_mode_entry *, const char *); static void window_copy_update_cursor(struct window_mode_entry *, u_int, u_int); @@ -275,6 +276,7 @@ struct window_copy_mode_data { int showmark; int searchtype; + int searchdirection; int searchregex; char *searchstr; u_char *searchmark; @@ -1718,10 +1720,10 @@ window_copy_cmd_search_again(struct window_copy_cmd_state *cs) if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) - window_copy_search_up(wme, data->searchregex, 1); + window_copy_search_up(wme, data->searchregex); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_down(wme, data->searchregex, 1); + window_copy_search_down(wme, data->searchregex); } return (WINDOW_COPY_CMD_NOTHING); } @@ -1735,10 +1737,10 @@ window_copy_cmd_search_reverse(struct window_copy_cmd_state *cs) if (data->searchtype == WINDOW_COPY_SEARCHUP) { for (; np != 0; np--) - window_copy_search_down(wme, data->searchregex, 1); + window_copy_search_down(wme, data->searchregex); } else if (data->searchtype == WINDOW_COPY_SEARCHDOWN) { for (; np != 0; np--) - window_copy_search_up(wme, data->searchregex, 1); + window_copy_search_up(wme, data->searchregex); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2042,7 +2044,7 @@ window_copy_cmd_search_backward(struct window_copy_cmd_state *cs) data->searchregex = 1; data->timeout = 0; for (; np != 0; np--) - window_copy_search_up(wme, 1, 0); + window_copy_search_up(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2062,7 +2064,7 @@ window_copy_cmd_search_backward_text(struct window_copy_cmd_state *cs) data->searchregex = 0; data->timeout = 0; for (; np != 0; np--) - window_copy_search_up(wme, 0, 0); + window_copy_search_up(wme, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2082,7 +2084,7 @@ window_copy_cmd_search_forward(struct window_copy_cmd_state *cs) data->searchregex = 1; data->timeout = 0; for (; np != 0; np--) - window_copy_search_down(wme, 1, 0); + window_copy_search_down(wme, 1); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2102,7 +2104,7 @@ window_copy_cmd_search_forward_text(struct window_copy_cmd_state *cs) data->searchregex = 0; data->timeout = 0; for (; np != 0; np--) - window_copy_search_down(wme, 0, 0); + window_copy_search_down(wme, 0); } return (WINDOW_COPY_CMD_NOTHING); } @@ -2143,7 +2145,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme, 0, 1)) { + if (!window_copy_search_up(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2153,7 +2155,7 @@ window_copy_cmd_search_backward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme, 0, 0)) { + if (!window_copy_search_down(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2198,7 +2200,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_down(wme, 0, 1)) { + if (!window_copy_search_down(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2208,7 +2210,7 @@ window_copy_cmd_search_forward_incremental(struct window_copy_cmd_state *cs) data->searchregex = 0; free(data->searchstr); data->searchstr = xstrdup(argument); - if (!window_copy_search_up(wme, 0, 1)) { + if (!window_copy_search_up(wme, 0)) { window_copy_clear_marks(wme); return (WINDOW_COPY_CMD_REDRAW); } @@ -2917,6 +2919,23 @@ window_copy_move_left(struct screen *s, u_int *fx, u_int *fy, int wrapflag) *fx = *fx - 1; } +static void +window_copy_move_right(struct screen *s, u_int *fx, u_int *fy, int wrapflag) +{ + if (*fx == screen_size_x(s) - 1) { /* right */ + if (*fy == screen_hsize(s) + screen_size_y(s) - 1) { /* bottom */ + if (wrapflag) { + *fx = 0; + *fy = 0; + } + return; + } + *fx = 0; + *fy = *fy + 1; + } else + *fx = *fx + 1; +} + static int window_copy_is_lowercase(const char *ptr) { @@ -2979,7 +2998,7 @@ window_copy_search_back_overlap(struct grid *gd, regex_t *preg, u_int *ppx, static int window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, struct grid *sgd, u_int fx, u_int fy, u_int endline, int cis, int wrap, - int direction, int regex, u_int *foundlen) + int direction, int regex) { u_int i, px, sx, ssize = 1; int found = 0, cflags = REG_EXTENDED; @@ -3004,20 +3023,15 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, if (regex) { found = window_copy_search_lr_regex(gd, &px, &sx, i, fx, gd->sx, ®); - if (found) - *foundlen = sx; } else { found = window_copy_search_lr(gd, sgd, &px, i, fx, gd->sx, cis); - if (found) - *foundlen = sgd->sx; } if (found) break; fx = 0; } } else { - *foundlen = 0; for (i = fy + 1; endline < i; i--) { if (regex) { found = window_copy_search_rl_regex(gd, @@ -3048,18 +3062,40 @@ window_copy_search_jump(struct window_mode_entry *wme, struct grid *gd, return (window_copy_search_jump(wme, gd, sgd, direction ? 0 : gd->sx - 1, direction ? 0 : gd->hsize + gd->sy - 1, fy, cis, 0, - direction, regex, foundlen)); + direction, regex)); } return (0); } +static void +window_copy_move_after_search_mark(struct window_copy_mode_data *data, + u_int *fx, u_int *fy, int wrapflag) +{ + struct screen *s = data->backing; + u_int at, start; + + if (window_copy_search_mark_at(data, *fx, *fy, &start) == 0 && + data->searchmark[start] != 0) { + while (window_copy_search_mark_at(data, *fx, *fy, &at) == 0) { + if (data->searchmark[at] != data->searchmark[start]) + break; + /* Stop if not wrapping and at the end of the grid. */ + if (!wrapflag && + *fx == screen_size_x(s) - 1 && + *fy == screen_hsize(s) + screen_size_y(s) - 1) + break; + + window_copy_move_right(s, fx, fy, wrapflag); + } + } +} + /* * Search in for text searchstr. If direction is 0 then search up, otherwise * down. */ static int -window_copy_search(struct window_mode_entry *wme, int direction, int regex, - int again) +window_copy_search(struct window_mode_entry *wme, int direction, int regex) { struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; @@ -3067,12 +3103,15 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex, struct screen_write_ctx ctx; struct grid *gd = s->grid; const char *str = data->searchstr; - u_int fx, fy, endline, i, foundlen; - int wrapflag, cis, found, visible_only; + u_int at, endline, fx, fy, start; + int cis, found, keys, visible_only; + int wrapflag; if (regex && str[strcspn(str, "^$*+()?[].\\")] == '\0') regex = 0; + data->searchdirection = direction; + if (data->timeout) return (0); @@ -3097,27 +3136,94 @@ window_copy_search(struct window_mode_entry *wme, int direction, int regex, wrapflag = options_get_number(wp->window->options, "wrap-search"); cis = window_copy_is_lowercase(str); - if (direction) + keys = options_get_number(wp->window->options, "mode-keys"); + + if (direction) { + /* + * Behave according to mode-keys. If it is emacs, search forward + * leaves the cursor after the match. If it is vi, the cursor + * remains at the beginning of the match, regardless of + * direction, which means that we need to start the next search + * after the term the cursor is currently on when searching + * forward. + */ + if (keys == MODEKEY_VI) { + if (data->searchmark != NULL) + window_copy_move_after_search_mark(data, &fx, + &fy, wrapflag); + else { + /* + * When there are no search marks, start the + * search after the current cursor position. + */ + window_copy_move_right(s, &fx, &fy, wrapflag); + } + } endline = gd->hsize + gd->sy - 1; + } else { - if (again) - window_copy_move_left(s, &fx, &fy, wrapflag); + window_copy_move_left(s, &fx, &fy, wrapflag); endline = 0; } found = window_copy_search_jump(wme, gd, ss.grid, fx, fy, endline, cis, - wrapflag, direction, regex, &foundlen); + wrapflag, direction, regex); if (found) { window_copy_search_marks(wme, &ss, regex, visible_only); - if (foundlen != 0) { - /* Adjust for wrapped lines eating one right. */ - i = data->cx + foundlen; - while (i > gd->sx - 1) { - i -= gd->sx; - window_copy_cursor_right(wme, 1); + fx = data->cx; + fy = screen_hsize(data->backing) - data->oy + data->cy; + + /* + * When searching forward, if the cursor is not at the beginning + * of the mark, search again. + */ + if (direction && + window_copy_search_mark_at(data, fx, fy, &at) == 0 && + at > 0 && + data->searchmark[at] == data->searchmark[at - 1]) { + window_copy_move_after_search_mark(data, &fx, &fy, + wrapflag); + window_copy_search_jump(wme, gd, ss.grid, fx, + fy, endline, cis, wrapflag, direction, + regex); + fx = data->cx; + fy = screen_hsize(data->backing) - data->oy + data->cy; + } + + if (direction) { + /* + * When in Emacs mode, position the cursor just after + * the mark. + */ + if (keys == MODEKEY_EMACS) { + window_copy_move_after_search_mark(data, &fx, + &fy, wrapflag); + data->cx = fx; + data->cy = fy - screen_hsize(data->backing) + + data-> oy; + } + } + else { + /* + * When searching backward, position the cursor at the + * beginning of the mark. + */ + if (window_copy_search_mark_at(data, fx, fy, + &start) == 0) { + while (window_copy_search_mark_at(data, fx, fy, + &at) == 0 && + data->searchmark[at] == + data->searchmark[start]) { + data->cx = fx; + data->cy = fy - + screen_hsize(data->backing) + + data-> oy; + if (at == 0) + break; + + window_copy_move_left(s, &fx, &fy, 0); + } } - for (i = 0; i < foundlen; i++) - window_copy_cursor_right(wme, 1); } } window_copy_redraw_screen(wme); @@ -3302,15 +3408,15 @@ window_copy_clear_marks(struct window_mode_entry *wme) } static int -window_copy_search_up(struct window_mode_entry *wme, int regex, int again) +window_copy_search_up(struct window_mode_entry *wme, int regex) { - return (window_copy_search(wme, 0, regex, again)); + return (window_copy_search(wme, 0, regex)); } static int -window_copy_search_down(struct window_mode_entry *wme, int regex, int again) +window_copy_search_down(struct window_mode_entry *wme, int regex) { - return (window_copy_search(wme, 1, regex, again)); + return (window_copy_search(wme, 1, regex)); } static void @@ -3396,9 +3502,11 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, struct grid_cell *gc, const struct grid_cell *mgc, const struct grid_cell *cgc, const struct grid_cell *mkgc) { + struct window_pane *wp = wme->wp; struct window_copy_mode_data *data = wme->data; u_int mark, start, end, cy, cursor, current; int inv = 0, found = 0; + int keys; if (data->showmark && fy == data->my) { gc->attr = mkgc->attr; @@ -3425,13 +3533,16 @@ window_copy_update_style(struct window_mode_entry *wme, u_int fx, u_int fy, cy = screen_hsize(data->backing) - data->oy + data->cy; if (window_copy_search_mark_at(data, data->cx, cy, &cursor) == 0) { - if (data->searchmark[cursor] == mark) - found = 1; - else if (cursor != 0) { - cursor--; - if (data->searchmark[cursor] == mark) + keys = options_get_number(wp->window->options, "mode-keys"); + if (cursor != 0 && + keys == MODEKEY_EMACS && + data->searchdirection) { + if (data->searchmark[cursor - 1] == mark) { + cursor--; found = 1; - } + } + } else if (data->searchmark[cursor] == mark) + found = 1; if (found) { window_copy_match_start_end(data, cursor, &start, &end); if (current >= start && current <= end) { @@ -4180,23 +4291,19 @@ static void window_copy_cursor_back_to_indentation(struct window_mode_entry *wme) { struct window_copy_mode_data *data = wme->data; - u_int px, py, xx; - struct grid_cell gc; + struct screen *back_s = data->backing; + struct grid_reader gr; + u_int px, py, oldy, hsize; - px = 0; - py = screen_hsize(data->backing) + data->cy - data->oy; - xx = window_copy_find_length(wme, py); + px = data->cx; + hsize = screen_hsize(back_s); + py = hsize + data->cy - data->oy; + oldy = data->cy; - while (px < xx) { - grid_get_cell(data->backing->grid, px, py, &gc); - if (gc.data.size != 1 || *gc.data.data != ' ') - break; - px++; - } - - window_copy_update_cursor(wme, px, data->cy); - if (window_copy_update_selection(wme, 1, 0)) - window_copy_redraw_lines(wme, data->cy, 1); + grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_back_to_indentation(&gr); + grid_reader_get_cursor(&gr, &px, &py); + window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } static void @@ -4287,7 +4394,7 @@ window_copy_cursor_left(struct window_mode_entry *wme) oldy = data->cy; grid_reader_start(&gr, back_s->grid, px, py); - grid_reader_cursor_left(&gr); + grid_reader_cursor_left(&gr, 1); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, py); } @@ -4472,10 +4579,8 @@ window_copy_cursor_jump_back(struct window_mode_entry *wme) py = hsize + data->cy - data->oy; oldy = data->cy; - if (px > 0) - px--; - grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_left(&gr, 0); if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_up(wme, hsize, data->oy, oldy, px, @@ -4498,7 +4603,7 @@ window_copy_cursor_jump_to(struct window_mode_entry *wme) grid_reader_start(&gr, back_s->grid, px, py); if (grid_reader_cursor_jump(&gr, data->jumpchar)) { - grid_reader_cursor_left(&gr); + grid_reader_cursor_left(&gr, 1); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), data->oy, oldy, px, py, 0); @@ -4518,13 +4623,9 @@ window_copy_cursor_jump_to_back(struct window_mode_entry *wme) py = hsize + data->cy - data->oy; oldy = data->cy; - if (px > 0) - px--; - - if (px > 0) - px--; - grid_reader_start(&gr, back_s->grid, px, py); + grid_reader_cursor_left(&gr, 0); + grid_reader_cursor_left(&gr, 0); if (grid_reader_cursor_jump_back(&gr, data->jumpchar)) { grid_reader_cursor_right(&gr, 1, 0); grid_reader_get_cursor(&gr, &px, &py); @@ -4577,7 +4678,7 @@ window_copy_cursor_next_word_end_pos(struct window_mode_entry *wme, grid_reader_cursor_right(&gr, 0, 0); grid_reader_cursor_next_word_end(&gr, separators); if (keys == MODEKEY_VI) - grid_reader_cursor_left(&gr); + grid_reader_cursor_left(&gr, 1); grid_reader_get_cursor(&gr, &px, &py); *ppx = px; *ppy = py; @@ -4607,7 +4708,7 @@ window_copy_cursor_next_word_end(struct window_mode_entry *wme, grid_reader_cursor_right(&gr, 0, 0); grid_reader_cursor_next_word_end(&gr, separators); if (keys == MODEKEY_VI) - grid_reader_cursor_left(&gr); + grid_reader_cursor_left(&gr, 1); grid_reader_get_cursor(&gr, &px, &py); window_copy_acquire_cursor_down(wme, hsize, screen_size_y(back_s), data->oy, oldy, px, py, no_reset); diff --git a/window-customize.c b/window-customize.c index a1f191b5..34a13f73 100644 --- a/window-customize.c +++ b/window-customize.c @@ -890,8 +890,8 @@ window_customize_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->data = mode_tree_start(wp, args, window_customize_build, window_customize_draw, NULL, window_customize_menu, - window_customize_height, data, window_customize_menu_items, NULL, 0, - &s); + window_customize_height, NULL, data, window_customize_menu_items, + NULL, 0, &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); @@ -999,7 +999,7 @@ window_customize_set_option_callback(struct client *c, void *itemdata, fail: *cause = toupper((u_char)*cause); - status_message_set(c, -1, 1, "%s", cause); + status_message_set(c, -1, 1, 0, "%s", cause); free(cause); return (0); } @@ -1205,7 +1205,7 @@ window_customize_set_command_callback(struct client *c, void *itemdata, fail: *error = toupper((u_char)*error); - status_message_set(c, -1, 1, "%s", error); + status_message_set(c, -1, 1, 0, "%s", error); free(error); return (0); } diff --git a/window-tree.c b/window-tree.c index cc822ac3..33af0413 100644 --- a/window-tree.c +++ b/window-tree.c @@ -56,6 +56,17 @@ static void window_tree_key(struct window_mode_entry *, "}" \ "}" +#define WINDOW_TREE_DEFAULT_KEY_FORMAT \ + "#{?#{e|<:#{line},10}," \ + "#{line}" \ + "," \ + "#{?#{e|<:#{line},36}," \ + "M-#{a:#{e|+:97,#{e|-:#{line},10}}}" \ + "," \ + "" \ + "}" \ + "}" + static const struct menu_item window_tree_menu_items[] = { { "Select", '\r', NULL }, { "Expand", KEYC_RIGHT, NULL }, @@ -117,6 +128,7 @@ struct window_tree_modedata { struct mode_tree_data *data; char *format; + char *key_format; char *command; int squash_groups; @@ -856,6 +868,35 @@ window_tree_menu(void *modedata, struct client *c, key_code key) window_tree_key(wme, c, NULL, NULL, key, NULL); } +static key_code +window_tree_get_key(void *modedata, void *itemdata, u_int line) +{ + struct window_tree_modedata *data = modedata; + struct window_tree_itemdata *item = itemdata; + struct format_tree *ft; + struct session *s; + struct winlink *wl; + struct window_pane *wp; + char *expanded; + key_code key; + + ft = format_create(NULL, NULL, FORMAT_NONE, 0); + window_tree_pull_item(item, &s, &wl, &wp); + if (item->type == WINDOW_TREE_SESSION) + format_defaults(ft, NULL, s, NULL, NULL); + else if (item->type == WINDOW_TREE_WINDOW) + format_defaults(ft, NULL, s, wl, NULL); + else + format_defaults(ft, NULL, s, wl, wp); + format_add(ft, "line", "%u", line); + + expanded = format_expand(ft, data->key_format); + key = key_string_lookup_string(expanded); + free(expanded); + format_free(ft); + return key; +} + static struct screen * window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) @@ -880,6 +921,10 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->format = xstrdup(WINDOW_TREE_DEFAULT_FORMAT); else data->format = xstrdup(args_get(args, 'F')); + if (args == NULL || !args_has(args, 'K')) + data->key_format = xstrdup(WINDOW_TREE_DEFAULT_KEY_FORMAT); + else + data->key_format = xstrdup(args_get(args, 'K')); if (args == NULL || args->argc == 0) data->command = xstrdup(WINDOW_TREE_DEFAULT_COMMAND); else @@ -887,9 +932,9 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->squash_groups = !args_has(args, 'G'); data->data = mode_tree_start(wp, args, window_tree_build, - window_tree_draw, window_tree_search, window_tree_menu, NULL, data, - window_tree_menu_items, window_tree_sort_list, - nitems(window_tree_sort_list), &s); + window_tree_draw, window_tree_search, window_tree_menu, NULL, + window_tree_get_key, data, window_tree_menu_items, + window_tree_sort_list, nitems(window_tree_sort_list), &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); @@ -913,6 +958,7 @@ window_tree_destroy(struct window_tree_modedata *data) free(data->item_list); free(data->format); + free(data->key_format); free(data->command); free(data);