diff --git a/cmd-bind-key.c b/cmd-bind-key.c index 41d8eeb8..041f558d 100644 --- a/cmd-bind-key.c +++ b/cmd-bind-key.c @@ -18,6 +18,8 @@ #include +#include + #include "tmux.h" /* @@ -29,15 +31,21 @@ int cmd_bind_key_exec(struct cmd *, struct cmd_ctx *); void cmd_bind_key_free(struct cmd *); size_t cmd_bind_key_print(struct cmd *, char *, size_t); +int cmd_bind_key_table(struct cmd *, struct cmd_ctx *); + struct cmd_bind_key_data { int key; int can_repeat; struct cmd_list *cmdlist; + + int command_key; + char *tablename; + char *modecmd; }; const struct cmd_entry cmd_bind_key_entry = { "bind-key", "bind", - "[-nr] key command [arguments]", + "[-cnr] [-t key-table] key command [arguments]", 0, 0, NULL, cmd_bind_key_parse, @@ -55,15 +63,24 @@ cmd_bind_key_parse(struct cmd *self, int argc, char **argv, char **cause) self->data = data = xmalloc(sizeof *data); data->can_repeat = 0; data->cmdlist = NULL; + data->command_key = 0; + data->tablename = NULL; + data->modecmd = NULL; - while ((opt = getopt(argc, argv, "nr")) != -1) { + while ((opt = getopt(argc, argv, "cnrt:")) != -1) { switch (opt) { + case 'c': + data->command_key = 1; + break; case 'n': no_prefix = 1; break; case 'r': data->can_repeat = 1; break; + case 't': + data->tablename = xstrdup(optarg); + break; default: goto usage; } @@ -82,8 +99,14 @@ cmd_bind_key_parse(struct cmd *self, int argc, char **argv, char **cause) argc--; argv++; - if ((data->cmdlist = cmd_list_parse(argc, argv, cause)) == NULL) - goto error; + if (data->tablename != NULL) { + if (argc != 1) + goto usage; + data->modecmd = xstrdup(argv[0]); + } else { + if ((data->cmdlist = cmd_list_parse(argc, argv, cause)) == NULL) + goto error; + } return (0); @@ -102,6 +125,8 @@ cmd_bind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx) if (data == NULL) return (0); + if (data->tablename != NULL) + return (cmd_bind_key_table(self, ctx)); key_bindings_add(data->key, data->can_repeat, data->cmdlist); data->cmdlist = NULL; /* avoid free */ @@ -109,6 +134,39 @@ cmd_bind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx) return (0); } +int +cmd_bind_key_table(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_bind_key_data *data = self->data; + const struct mode_key_table *mtab; + struct mode_key_binding *mbind, mtmp; + enum mode_key_cmd cmd; + + if ((mtab = mode_key_findtable(data->tablename)) == NULL) { + ctx->error(ctx, "unknown key table: %s", data->tablename); + return (-1); + } + + cmd = mode_key_fromstring(mtab->cmdstr, data->modecmd); + if (cmd == MODEKEY_NONE) { + ctx->error(ctx, "unknown command: %s", data->modecmd); + return (-1); + } + + mtmp.key = data->key & ~KEYC_PREFIX; + mtmp.mode = data->command_key ? 1 : 0; + if ((mbind = SPLAY_FIND(mode_key_tree, mtab->tree, &mtmp)) != NULL) { + mbind->cmd = cmd; + return (0); + } + mbind = xmalloc(sizeof *mbind); + mbind->key = mtmp.key; + mbind->mode = mtmp.mode; + mbind->cmd = cmd; + SPLAY_INSERT(mode_key_tree, mtab->tree, mbind); + return (0); +} + void cmd_bind_key_free(struct cmd *self) { @@ -116,6 +174,10 @@ cmd_bind_key_free(struct cmd *self) if (data->cmdlist != NULL) cmd_list_free(data->cmdlist); + if (data->tablename != NULL) + xfree(data->tablename); + if (data->modecmd != NULL) + xfree(data->modecmd); xfree(data); } diff --git a/cmd-list-keys.c b/cmd-list-keys.c index 0a22e82c..db64e244 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -92,11 +92,7 @@ cmd_list_keys_table(struct cmd *self, struct cmd_ctx *ctx) const char *key, *cmdstr, *mode; int width, keywidth; - for (mtab = mode_key_tables; mtab->name != NULL; mtab++) { - if (strcasecmp(data->target, mtab->name) == 0) - break; - } - if (mtab->name == NULL) { + if ((mtab = mode_key_findtable(data->target)) == NULL) { ctx->error(ctx, "unknown key table: %s", data->target); return (-1); } diff --git a/cmd-unbind-key.c b/cmd-unbind-key.c index 35c43658..8f5e794e 100644 --- a/cmd-unbind-key.c +++ b/cmd-unbind-key.c @@ -28,13 +28,18 @@ int cmd_unbind_key_parse(struct cmd *, int, char **, char **); int cmd_unbind_key_exec(struct cmd *, struct cmd_ctx *); void cmd_unbind_key_free(struct cmd *); +int cmd_unbind_key_table(struct cmd *, struct cmd_ctx *); + struct cmd_unbind_key_data { int key; + + int command_key; + char *tablename; }; const struct cmd_entry cmd_unbind_key_entry = { "unbind-key", "unbind", - "[-n] key", + "[-cn] [-t key-table] key", 0, 0, NULL, cmd_unbind_key_parse, @@ -50,12 +55,20 @@ cmd_unbind_key_parse(struct cmd *self, int argc, char **argv, char **cause) int opt, no_prefix = 0; self->data = data = xmalloc(sizeof *data); + data->command_key = 0; + data->tablename = NULL; - while ((opt = getopt(argc, argv, "n")) != -1) { + while ((opt = getopt(argc, argv, "cnt:")) != -1) { switch (opt) { + case 'c': + data->command_key = 1; + break; case 'n': no_prefix = 1; break; + case 't': + data->tablename = xstrdup(optarg); + break; default: goto usage; } @@ -89,16 +102,41 @@ cmd_unbind_key_exec(struct cmd *self, unused struct cmd_ctx *ctx) if (data == NULL) return (0); + if (data->tablename != NULL) + return (cmd_unbind_key_table(self, ctx)); key_bindings_remove(data->key); return (0); } +int +cmd_unbind_key_table(struct cmd *self, struct cmd_ctx *ctx) +{ + struct cmd_unbind_key_data *data = self->data; + const struct mode_key_table *mtab; + struct mode_key_binding *mbind, mtmp; + + if ((mtab = mode_key_findtable(data->tablename)) == NULL) { + ctx->error(ctx, "unknown key table: %s", data->tablename); + return (-1); + } + + mtmp.key = data->key & ~KEYC_PREFIX; + mtmp.mode = data->command_key ? 1 : 0; + if ((mbind = SPLAY_FIND(mode_key_tree, mtab->tree, &mtmp)) != NULL) { + SPLAY_REMOVE(mode_key_tree, mtab->tree, mbind); + xfree(mbind); + } + return (0); +} + void cmd_unbind_key_free(struct cmd *self) { struct cmd_unbind_key_data *data = self->data; + if (data->tablename != NULL) + xfree(data->tablename); xfree(data); } diff --git a/mode-key.c b/mode-key.c index e0f773d5..ccd5465c 100644 --- a/mode-key.c +++ b/mode-key.c @@ -18,6 +18,8 @@ #include +#include + #include "tmux.h" /* @@ -54,6 +56,8 @@ struct mode_key_cmdstr mode_key_cmdstr_edit[] = { { MODEKEYEDIT_STARTOFLINE, "start-of-line" }, { MODEKEYEDIT_SWITCHMODE, "switch-mode" }, { MODEKEYEDIT_SWITCHMODEAPPEND, "switch-mode-append" }, + + { 0, NULL } }; /* Choice keys command strings. */ @@ -64,6 +68,8 @@ struct mode_key_cmdstr mode_key_cmdstr_choice[] = { { MODEKEYCHOICE_PAGEDOWN, "page-down" }, { MODEKEYCHOICE_PAGEUP, "page-up" }, { MODEKEYCHOICE_UP, "up" }, + + { 0, NULL } }; /* Copy keys command strings. */ @@ -83,6 +89,8 @@ struct mode_key_cmdstr mode_key_cmdstr_copy[] = { { MODEKEYCOPY_STARTOFLINE, "start-of-line" }, { MODEKEYCOPY_STARTSELECTION, "begin-selection" }, { MODEKEYCOPY_UP, "cursor-up" }, + + { 0, NULL } }; /* vi editing keys. */ @@ -278,6 +286,28 @@ mode_key_tostring(struct mode_key_cmdstr *cmdstr, enum mode_key_cmd cmd) return (NULL); } +enum mode_key_cmd +mode_key_fromstring(struct mode_key_cmdstr *cmdstr, const char *name) +{ + for (; cmdstr->name != NULL; cmdstr++) { + if (strcasecmp(cmdstr->name, name) == 0) + return (cmdstr->cmd); + } + return (MODEKEY_NONE); +} + +const struct mode_key_table * +mode_key_findtable(const char *name) +{ + const struct mode_key_table *mtab; + + for (mtab = mode_key_tables; mtab->name != NULL; mtab++) { + if (strcasecmp(name, mtab->name) == 0) + return (mtab); + } + return (NULL); +} + void mode_key_init_trees(void) { diff --git a/tmux.1 b/tmux.1 index 1a3b1664..06bc76bd 100644 --- a/tmux.1 +++ b/tmux.1 @@ -352,7 +352,10 @@ and used in copy and scroll modes. The tables may be viewed with the .Ic list-keys -command. +command and keys modified or removed with +.Ic bind-key +and +.Ic unbind-key . .Pp The paste buffer key pastes the first line from the top paste buffer on the stack. @@ -637,7 +640,8 @@ If no server is started, will attempt to start it; this will fail unless sessions are created in the configuration file. .It Xo Ic bind-key -.Op Fl nr +.Op Fl cnr +.Op Fl t Ar key-table .Ar key Ar command Op Ar arguments .Xc .D1 (alias: Ic bind ) @@ -652,7 +656,11 @@ or for Ctrl keys, or .Ql M- for Alt (meta) keys. -If +.Pp +By default (without +.Fl t ) +the primary key bindings are modified (those normally activated with the prefix +key); in this case, if .Fl n is specified, it is not necessary to use the prefix key, .Ar command @@ -664,6 +672,19 @@ The flag indicates this key may repeat, see the .Ic repeat-time option. +.Pp +If +.Fl t +is present, +.Ar key +is bound in +.Ar key-table : +the binding for command mode with +.Fl c +or for normal mode without. +To view the default bindings and possible commands, see the +.Ic list-keys +command. .It Xo Ic break-pane .Op Fl d .Op Fl p Ar pane-index @@ -866,7 +887,7 @@ List all clients attached to the server. List the syntax of all commands supported by .Nm . .It Xo Ic list-keys -.Op Ar Fl t Ar key-table +.Op Fl t Ar key-table .Xc .D1 (alias: Ic lsk ) List all key bindings. @@ -1665,17 +1686,30 @@ Switch the current session for client to .Ar target-session . .It Xo Ic unbind-key -.Op Fl n +.Op Fl cn +.Op Fl t Ar key-table .Ar key .Xc .D1 (alias: Ic unbind ) Unbind the command bound to .Ar key . -If +Without +.Fl t +the primary key bindings are modified; in this case, if .Fl n is specified, the command bound to .Ar key without a prefix (if any) is removed. +.Pp +If +.Fl t +is present, +.Ar key +in +.Ar key-table +is unbound: the binding for command mode with +.Fl c +or for normal mode without. .It Xo Ic unlink-window .Op Fl k .Op Fl t Ar target-window diff --git a/tmux.h b/tmux.h index 9d5898ae..7fc2117c 100644 --- a/tmux.h +++ b/tmux.h @@ -1098,6 +1098,8 @@ extern struct mode_key_tree mode_key_tree_emacs_copy; int mode_key_cmp(struct mode_key_binding *, struct mode_key_binding *); SPLAY_PROTOTYPE(mode_key_tree, mode_key_binding, entry, mode_key_cmp); const char *mode_key_tostring(struct mode_key_cmdstr *r, enum mode_key_cmd); +enum mode_key_cmd mode_key_fromstring(struct mode_key_cmdstr *, const char *); +const struct mode_key_table *mode_key_findtable(const char *); void mode_key_init_trees(void); void mode_key_free_trees(void); void mode_key_init(struct mode_key_data *, struct mode_key_tree *);