diff --git a/Makefile.am b/Makefile.am index b0cf0ffc..7dddbe49 100644 --- a/Makefile.am +++ b/Makefile.am @@ -106,6 +106,7 @@ dist_tmux_SOURCES = \ cmd-kill-window.c \ cmd-list-buffers.c \ cmd-list-clients.c \ + cmd-list-commands.c \ cmd-list-keys.c \ cmd-list-panes.c \ cmd-list-sessions.c \ diff --git a/cmd-list-commands.c b/cmd-list-commands.c new file mode 100644 index 00000000..0b0f912d --- /dev/null +++ b/cmd-list-commands.c @@ -0,0 +1,107 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2007 Nicholas Marriott + * + * 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 + +#include +#include + +#include "tmux.h" + +/* + * List all commands. + */ + +#define LIST_COMMANDS_TEMPLATE \ + "#{command_list_name}" \ + "#{?command_list_alias, (#{command_list_alias}),} " \ + "#{command_list_usage}" + +static enum cmd_retval cmd_list_commands(struct cmd *, struct cmdq_item *); + +const struct cmd_entry cmd_list_commands_entry = { + .name = "list-commands", + .alias = "lscm", + + .args = { "F:", 0, 1, NULL }, + .usage = "[-F format] [command]", + + .flags = CMD_STARTSERVER|CMD_AFTERHOOK, + .exec = cmd_list_commands +}; + +static void +cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft, + const char *template, struct cmdq_item *item) +{ + const char *s; + char *line; + + format_add(ft, "command_list_name", "%s", entry->name); + if (entry->alias != NULL) + s = entry->alias; + else + s = ""; + format_add(ft, "command_list_alias", "%s", s); + if (entry->usage != NULL) + s = entry->usage; + else + s = ""; + format_add(ft, "command_list_usage", "%s", s); + + line = format_expand(ft, template); + if (*line != '\0') + cmdq_print(item, "%s", line); + free(line); +} + +static enum cmd_retval +cmd_list_commands(struct cmd *self, struct cmdq_item *item) +{ + struct args *args = cmd_get_args(self); + const struct cmd_entry **entryp; + const struct cmd_entry *entry; + struct format_tree *ft; + const char *template, *command; + char *cause; + + if ((template = args_get(args, 'F')) == NULL) + template = LIST_COMMANDS_TEMPLATE; + + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); + format_defaults(ft, NULL, NULL, NULL, NULL); + + command = args_string(args, 0); + if (command == NULL) { + for (entryp = cmd_table; *entryp != NULL; entryp++) + cmd_list_single_command(*entryp, ft, template, item); + } else { + entry = cmd_find(command, &cause); + if (entry != NULL) + cmd_list_single_command(entry, ft, template, item); + else { + cmdq_error(item, "%s", cause); + free(cause); + format_free(ft); + return (CMD_RETURN_ERROR); + } + } + + format_free(ft); + return (CMD_RETURN_NORMAL); +} diff --git a/cmd-list-keys.c b/cmd-list-keys.c index f25b0636..93d4df7c 100644 --- a/cmd-list-keys.c +++ b/cmd-list-keys.c @@ -27,122 +27,140 @@ * List key bindings. */ -static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *); +#define LIST_KEYS_TEMPLATE \ + "#{?notes_only," \ + "#{key_prefix} " \ + "#{p|#{key_string_width}:key_string} " \ + "#{?key_note,#{key_note},#{key_command}}" \ + "," \ + "bind-key #{?key_has_repeat,#{?key_repeat,-r, },} " \ + "-T #{p|#{key_table_width}:key_table} " \ + "#{p|#{key_string_width}:key_string} " \ + "#{key_command}}" -static enum cmd_retval cmd_list_keys_commands(struct cmd *, - struct cmdq_item *); +static enum cmd_retval cmd_list_keys_exec(struct cmd *, struct cmdq_item *); const struct cmd_entry cmd_list_keys_entry = { .name = "list-keys", .alias = "lsk", - .args = { "1aNP:T:", 0, 1, NULL }, - .usage = "[-1aN] [-P prefix-string] [-T key-table] [key]", + .args = { "1aF:NO:P:rT:", 0, 1, NULL }, + .usage = "[-1aNr] [-F format] [-O order] [-P prefix-string]" + "[-T key-table] [key]", .flags = CMD_STARTSERVER|CMD_AFTERHOOK, .exec = cmd_list_keys_exec }; -const struct cmd_entry cmd_list_commands_entry = { - .name = "list-commands", - .alias = "lscm", +static char * +cmd_list_keys_get_prefix(struct args *args) +{ + key_code prefix; - .args = { "F:", 0, 1, NULL }, - .usage = "[-F format] [command]", + if (args_has(args, 'P')) + return (xstrdup(args_get(args, 'P'))); - .flags = CMD_STARTSERVER|CMD_AFTERHOOK, - .exec = cmd_list_keys_exec -}; + prefix = options_get_number(global_s_options, "prefix"); + if (prefix == KEYC_NONE) + return (xstrdup("")); + return (xstrdup(key_string_lookup_key(prefix, 0))); +} static u_int -cmd_list_keys_get_width(const char *tablename, key_code only) +cmd_list_keys_get_width(struct key_binding **l, u_int n) { - struct key_table *table; - struct key_binding *bd; - u_int width, keywidth = 0; + u_int i, width, keywidth = 0; - table = key_bindings_get_table(tablename, 0); - if (table == NULL) - return (0); - bd = key_bindings_first(table); - while (bd != NULL) { - if ((only != KEYC_UNKNOWN && bd->key != only) || - KEYC_IS_MOUSE(bd->key) || - bd->note == NULL || - *bd->note == '\0') { - bd = key_bindings_next(table, bd); - continue; - } - width = utf8_cstrwidth(key_string_lookup_key(bd->key, 0)); + for (i = 0; i < n; i++) { + width = utf8_cstrwidth(key_string_lookup_key(l[i]->key, 0)); if (width > keywidth) keywidth = width; - - bd = key_bindings_next(table, bd); } return (keywidth); } -static int -cmd_list_keys_print_notes(struct cmdq_item *item, struct args *args, - const char *tablename, u_int keywidth, key_code only, const char *prefix) +static u_int +cmd_list_keys_get_table_width(struct key_binding **l, u_int n) { - struct client *tc = cmdq_get_target_client(item); - struct key_table *table; - struct key_binding *bd; - const char *key; - char *tmp, *note; - int found = 0; + u_int i, width, tablewidth = 0; - table = key_bindings_get_table(tablename, 0); - if (table == NULL) - return (0); - bd = key_bindings_first(table); - while (bd != NULL) { - if ((only != KEYC_UNKNOWN && bd->key != only) || - KEYC_IS_MOUSE(bd->key) || - ((bd->note == NULL || *bd->note == '\0') && - !args_has(args, 'a'))) { - bd = key_bindings_next(table, bd); - continue; - } - found = 1; - key = key_string_lookup_key(bd->key, 0); - - if (bd->note == NULL || *bd->note == '\0') - note = cmd_list_print(bd->cmdlist, - CMD_LIST_PRINT_ESCAPED|CMD_LIST_PRINT_NO_GROUPS); - else - note = xstrdup(bd->note); - tmp = utf8_padcstr(key, keywidth + 1); - if (args_has(args, '1') && tc != NULL) { - status_message_set(tc, -1, 1, 0, 0, "%s%s%s", prefix, - tmp, note); - } else - cmdq_print(item, "%s%s%s", prefix, tmp, note); - free(tmp); - free(note); - - if (args_has(args, '1')) - break; - bd = key_bindings_next(table, bd); + for (i = 0; i < n; i++) { + width = utf8_cstrwidth(l[i]->tablename); + if (width > tablewidth) + tablewidth = width; } - return (found); + return (tablewidth); } -static char * -cmd_list_keys_get_prefix(struct args *args, key_code *prefix) +static struct key_binding ** +cmd_get_root_and_prefix(u_int *n, struct sort_criteria *sort_crit) { - char *s; + const char *tables[] = { "prefix", "root" }; + struct key_table *t; + struct key_binding **lt; + u_int i, ltsz, len = 0, offset = 0; + static struct key_binding **l = NULL; + static u_int lsz = 0; - *prefix = options_get_number(global_s_options, "prefix"); - if (!args_has(args, 'P')) { - if (*prefix != KEYC_NONE) - xasprintf(&s, "%s ", key_string_lookup_key(*prefix, 0)); - else - s = xstrdup(""); - } else - s = xstrdup(args_get(args, 'P')); - return (s); + for (i = 0; i < nitems(tables); i++) { + t = key_bindings_get_table(tables[i], 0); + lt = sort_get_key_bindings_table(t, <sz, sort_crit); + len += ltsz; + if (lsz <= len) { + lsz = len + 100; + l = xreallocarray(l, lsz, sizeof *l); + } + memcpy(l + offset, lt, ltsz * sizeof *l); + offset += ltsz; + } + + *n = len; + return (l); +} + +static void +cmd_filter_key_list(int filter_notes, int filter_key, key_code only, + struct key_binding **l, u_int *n) +{ + key_code key; + u_int i, j = 0; + + for (i = 0; i < *n; i++) { + key = l[i]->key & (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS); + if (filter_key && only != key) + continue; + if (filter_notes && l[i]->note == NULL) + continue; + l[j++] = l[i]; + } + *n = j; +} + +static void +cmd_format_add_key_binding(struct format_tree *ft, + const struct key_binding *bd, const char *prefix) +{ + const char *s; + + if (bd->flags & KEY_BINDING_REPEAT) + format_add(ft, "key_repeat", "1"); + else + format_add(ft, "key_repeat", "0"); + + if (bd->note != NULL) + format_add(ft, "key_note", "%s", bd->note); + else + format_add(ft, "key_note", "%s", ""); + + format_add(ft, "key_prefix", "%s", prefix); + format_add(ft, "key_table", "%s", bd->tablename); + + s = key_string_lookup_key(bd->key, 0); + format_add(ft, "key_string", "%s", s); + + s = cmd_list_print(bd->cmdlist, + CMD_LIST_PRINT_ESCAPED|CMD_LIST_PRINT_NO_GROUPS); + format_add(ft, "key_command", "%s", s); } static enum cmd_retval @@ -150,16 +168,16 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct client *tc = cmdq_get_target_client(item); - struct key_table *table; - struct key_binding *bd; - const char *tablename, *r, *keystr; - char *key, *cp, *tmp, *start, *empty; - key_code prefix, only = KEYC_UNKNOWN; - int repeat, width, tablewidth, keywidth, found = 0; - size_t tmpsize, tmpused, cplen; - - if (cmd_get_entry(self) == &cmd_list_commands_entry) - return (cmd_list_keys_commands(self, item)); + struct format_tree *ft; + struct key_table *table = NULL; + struct key_binding **l; + key_code only = KEYC_UNKNOWN; + const char *template, *tablename, *keystr; + char *line; + char *prefix = NULL; + u_int i, n; + int single, notes_only, filter_notes, filter_key; + struct sort_criteria sort_crit; if ((keystr = args_string(args, 0)) != NULL) { only = key_string_lookup_string(keystr); @@ -170,219 +188,60 @@ cmd_list_keys_exec(struct cmd *self, struct cmdq_item *item) only &= (KEYC_MASK_KEY|KEYC_MASK_MODIFIERS); } - tablename = args_get(args, 'T'); - if (tablename != NULL && key_bindings_get_table(tablename, 0) == NULL) { - cmdq_error(item, "table %s doesn't exist", tablename); - return (CMD_RETURN_ERROR); - } + sort_crit.order = sort_order_from_string(args_get(args, 'O')); + sort_crit.reversed = args_has(args, 'r'); - if (args_has(args, 'N')) { - if (tablename == NULL) { - start = cmd_list_keys_get_prefix(args, &prefix); - keywidth = cmd_list_keys_get_width("root", only); - if (prefix != KEYC_NONE) { - width = cmd_list_keys_get_width("prefix", only); - if (width == 0) - prefix = KEYC_NONE; - else if (width > keywidth) - keywidth = width; - } - empty = utf8_padcstr("", utf8_cstrwidth(start)); + prefix = cmd_list_keys_get_prefix(args); + single = args_has(args, '1'); + notes_only = args_has(args, 'N'); - found = cmd_list_keys_print_notes(item, args, "root", - keywidth, only, empty); - if (prefix != KEYC_NONE) { - if (cmd_list_keys_print_notes(item, args, - "prefix", keywidth, only, start)) - found = 1; - } - free(empty); - } else { - if (args_has(args, 'P')) - start = xstrdup(args_get(args, 'P')); - else - start = xstrdup(""); - keywidth = cmd_list_keys_get_width(tablename, only); - found = cmd_list_keys_print_notes(item, args, tablename, - keywidth, only, start); - - } - free(start); - goto out; - } - - repeat = 0; - tablewidth = keywidth = 0; - table = key_bindings_first_table(); - while (table != NULL) { - if (tablename != NULL && strcmp(table->name, tablename) != 0) { - table = key_bindings_next_table(table); - continue; - } - bd = key_bindings_first(table); - while (bd != NULL) { - if (only != KEYC_UNKNOWN && bd->key != only) { - bd = key_bindings_next(table, bd); - continue; - } - key = args_escape(key_string_lookup_key(bd->key, 0)); - - if (bd->flags & KEY_BINDING_REPEAT) - repeat = 1; - - width = utf8_cstrwidth(table->name); - if (width > tablewidth) - tablewidth = width; - width = utf8_cstrwidth(key); - if (width > keywidth) - keywidth = width; - - free(key); - bd = key_bindings_next(table, bd); - } - table = key_bindings_next_table(table); - } - - tmpsize = 256; - tmp = xmalloc(tmpsize); - - table = key_bindings_first_table(); - while (table != NULL) { - if (tablename != NULL && strcmp(table->name, tablename) != 0) { - table = key_bindings_next_table(table); - continue; - } - bd = key_bindings_first(table); - while (bd != NULL) { - if (only != KEYC_UNKNOWN && bd->key != only) { - bd = key_bindings_next(table, bd); - continue; - } - found = 1; - key = args_escape(key_string_lookup_key(bd->key, 0)); - - if (!repeat) - r = ""; - else if (bd->flags & KEY_BINDING_REPEAT) - r = "-r "; - else - r = " "; - tmpused = xsnprintf(tmp, tmpsize, "%s-T ", r); - - cp = utf8_padcstr(table->name, tablewidth); - cplen = strlen(cp) + 1; - while (tmpused + cplen + 1 >= tmpsize) { - tmpsize *= 2; - tmp = xrealloc(tmp, tmpsize); - } - strlcat(tmp, cp, tmpsize); - tmpused = strlcat(tmp, " ", tmpsize); - free(cp); - - cp = utf8_padcstr(key, keywidth); - cplen = strlen(cp) + 1; - while (tmpused + cplen + 1 >= tmpsize) { - tmpsize *= 2; - tmp = xrealloc(tmp, tmpsize); - } - strlcat(tmp, cp, tmpsize); - tmpused = strlcat(tmp, " ", tmpsize); - free(cp); - - cp = cmd_list_print(bd->cmdlist, - CMD_LIST_PRINT_ESCAPED|CMD_LIST_PRINT_NO_GROUPS); - cplen = strlen(cp); - while (tmpused + cplen + 1 >= tmpsize) { - tmpsize *= 2; - tmp = xrealloc(tmp, tmpsize); - } - strlcat(tmp, cp, tmpsize); - free(cp); - - if (args_has(args, '1') && tc != NULL) { - status_message_set(tc, -1, 1, 0, 0, - "bind-key %s", tmp); - } else - cmdq_print(item, "bind-key %s", tmp); - free(key); - - if (args_has(args, '1')) - break; - bd = key_bindings_next(table, bd); - } - table = key_bindings_next_table(table); - } - - free(tmp); - -out: - if (only != KEYC_UNKNOWN && !found) { - cmdq_error(item, "unknown key: %s", args_string(args, 0)); - return (CMD_RETURN_ERROR); - } - return (CMD_RETURN_NORMAL); -} - -static void -cmd_list_single_command(const struct cmd_entry *entry, struct format_tree *ft, - const char *template, struct cmdq_item *item) -{ - const char *s; - char *line; - - format_add(ft, "command_list_name", "%s", entry->name); - if (entry->alias != NULL) - s = entry->alias; - else - s = ""; - format_add(ft, "command_list_alias", "%s", s); - if (entry->usage != NULL) - s = entry->usage; - else - s = ""; - format_add(ft, "command_list_usage", "%s", s); - - line = format_expand(ft, template); - if (*line != '\0') - cmdq_print(item, "%s", line); - free(line); -} - -static enum cmd_retval -cmd_list_keys_commands(struct cmd *self, struct cmdq_item *item) -{ - struct args *args = cmd_get_args(self); - const struct cmd_entry **entryp; - const struct cmd_entry *entry; - struct format_tree *ft; - const char *template, *command; - char *cause; - - if ((template = args_get(args, 'F')) == NULL) { - template = "#{command_list_name}" - "#{?command_list_alias, (#{command_list_alias}),} " - "#{command_list_usage}"; - } - - ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); - format_defaults(ft, NULL, NULL, NULL, NULL); - - command = args_string(args, 0); - if (command == NULL) { - for (entryp = cmd_table; *entryp != NULL; entryp++) - cmd_list_single_command(*entryp, ft, template, item); - } else { - entry = cmd_find(command, &cause); - if (entry != NULL) - cmd_list_single_command(entry, ft, template, item); - else { - cmdq_error(item, "%s", cause); - free(cause); - format_free(ft); + if ((tablename = args_get(args, 'T')) != NULL) { + table = key_bindings_get_table(tablename, 0); + if (table == NULL) { + cmdq_error(item, "table %s doesn't exist", tablename); return (CMD_RETURN_ERROR); } } + if ((template = args_get(args, 'F')) == NULL) + template = LIST_KEYS_TEMPLATE; + + if (table) + l = sort_get_key_bindings_table(table, &n, &sort_crit); + else if (notes_only) + l = cmd_get_root_and_prefix(&n, &sort_crit); + else + l = sort_get_key_bindings(&n, &sort_crit); + + filter_notes = notes_only && !args_has(args, 'a'); + filter_key = only != KEYC_UNKNOWN; + if (filter_notes || filter_key) + cmd_filter_key_list(filter_notes, filter_key, only, l, &n); + if (single) + n = 1; + + ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); + format_defaults(ft, NULL, NULL, NULL, NULL); + format_add(ft, "notes_only", "%d", notes_only); + format_add(ft, "key_has_repeat", "%d", key_bindings_has_repeat(l, n)); + format_add(ft, "key_string_width", "%u", cmd_list_keys_get_width(l, n)); + format_add(ft, "key_table_width", "%u", + cmd_list_keys_get_table_width(l, n)); + for (i = 0; i < n; i++) { + cmd_format_add_key_binding(ft, l[i], prefix); + + line = format_expand(ft, template); + if ((single && tc != NULL) || n == 1) + status_message_set(tc, -1, 1, 0, 0, "%s", line); + else if (*line != '\0') + cmdq_print(item, "%s", line); + free(line); + + if (single) + break; + } format_free(ft); + free(prefix); + return (CMD_RETURN_NORMAL); } diff --git a/cmd-paste-buffer.c b/cmd-paste-buffer.c index 269e92f7..09bf95ac 100644 --- a/cmd-paste-buffer.c +++ b/cmd-paste-buffer.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_paste_buffer_entry = { .name = "paste-buffer", .alias = "pasteb", - .args = { "db:prs:t:", 0, 0, NULL }, - .usage = "[-dpr] [-s separator] " CMD_BUFFER_USAGE " " + .args = { "db:prSs:t:", 0, 0, NULL }, + .usage = "[-dprS] [-s separator] " CMD_BUFFER_USAGE " " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -43,6 +43,17 @@ const struct cmd_entry cmd_paste_buffer_entry = { .exec = cmd_paste_buffer_exec }; +static void +cmd_paste_buffer_paste(struct window_pane *wp, const char *buf, size_t len) +{ + char *cp; + size_t n; + + n = utf8_stravisx(&cp, buf, len, VIS_SAFE); + bufferevent_write(wp->event, cp, n); + free(cp); +} + static enum cmd_retval cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) { @@ -51,7 +62,7 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *wp = target->wp; struct paste_buffer *pb; const char *sepstr, *bufname, *bufdata, *bufend, *line; - size_t seplen, bufsize; + size_t seplen, bufsize, len; int bracket = args_has(args, 'p'); if (window_pane_exited(wp)) { @@ -93,14 +104,22 @@ cmd_paste_buffer_exec(struct cmd *self, struct cmdq_item *item) line = memchr(bufdata, '\n', bufend - bufdata); if (line == NULL) break; - - bufferevent_write(wp->event, bufdata, line - bufdata); + len = line - bufdata; + if (args_has(args, 'S')) + bufferevent_write(wp->event, bufdata, len); + else + cmd_paste_buffer_paste(wp, bufdata, len); bufferevent_write(wp->event, sepstr, seplen); bufdata = line + 1; } - if (bufdata != bufend) - bufferevent_write(wp->event, bufdata, bufend - bufdata); + if (bufdata != bufend) { + len = bufend - bufdata; + if (args_has(args, 'S')) + bufferevent_write(wp->event, bufdata, len); + else + cmd_paste_buffer_paste(wp, bufdata, len); + } if (bracket && (wp->screen->mode & MODE_BRACKETPASTE)) bufferevent_write(wp->event, "\033[201~", 6); diff --git a/format.c b/format.c index 283b83a4..4709e7a5 100644 --- a/format.c +++ b/format.c @@ -5239,11 +5239,13 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, done: /* Expand again if required. */ if (modifiers & FORMAT_EXPAND) { - new = format_expand1(es, value); + format_copy_state(&next, es, FORMAT_EXPAND_NOJOBS); + new = format_expand1(&next, value); free(value); value = new; } else if (modifiers & FORMAT_EXPANDTIME) { - format_copy_state(&next, es, FORMAT_EXPAND_TIME); + format_copy_state(&next, es, FORMAT_EXPAND_TIME| + FORMAT_EXPAND_NOJOBS); new = format_expand1(&next, value); free(value); value = new; diff --git a/input.c b/input.c index b76604f0..3c204e81 100644 --- a/input.c +++ b/input.c @@ -1625,10 +1625,6 @@ input_csi_dispatch(struct input_ctx *ictx) } input_reply(ictx, 1, "\033[?12;%d$y", n); break; - case 2004: /* bracketed paste */ - n = (s->mode & MODE_BRACKETPASTE) ? 1 : 2; - input_reply(ictx, 1, "\033[?2004;%d$y", n); - break; case 1004: /* focus reporting */ n = (s->mode & MODE_FOCUSON) ? 1 : 2; input_reply(ictx, 1, "\033[?1004;%d$y", n); @@ -1637,6 +1633,14 @@ input_csi_dispatch(struct input_ctx *ictx) n = (s->mode & MODE_MOUSE_SGR) ? 1 : 2; input_reply(ictx, 1, "\033[?1006;%d$y", n); break; + case 2004: /* bracketed paste */ + n = (s->mode & MODE_BRACKETPASTE) ? 1 : 2; + input_reply(ictx, 1, "\033[?2004;%d$y", n); + break; + case 2026: /* synchronized output */ + n = (s->mode & MODE_SYNC) ? 1 : 2; + input_reply(ictx, 1, "\033[?2026;%d$y", n); + break; case 2031: input_reply(ictx, 1, "\033[?2031;2$y"); break; diff --git a/key-bindings.c b/key-bindings.c index d30c3cdb..a395775f 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -215,6 +215,7 @@ key_bindings_add(const char *name, key_code key, const char *note, int repeat, bd = xcalloc(1, sizeof *bd); bd->key = (key & ~KEYC_MASK_FLAGS); + bd->tablename = table->name; if (note != NULL) bd->note = xstrdup(note); RB_INSERT(key_bindings, &table->key_bindings, bd); @@ -702,3 +703,15 @@ key_bindings_dispatch(struct key_binding *bd, struct cmdq_item *item, new_item = cmdq_append(c, new_item); return (new_item); } + +int +key_bindings_has_repeat(struct key_binding **l, u_int n) +{ + u_int i; + + for (i = 0; i < n; i++) { + if (l[i]->flags & KEY_BINDING_REPEAT) + return (1); + } + return (0); +} diff --git a/sort.c b/sort.c index fb3f89ee..1b18d370 100644 --- a/sort.c +++ b/sort.c @@ -72,6 +72,7 @@ sort_buffer_cmp(const void *a0, const void *b0) break; case SORT_ACTIVITY: case SORT_INDEX: + case SORT_MODIFIER: case SORT_ORDER: case SORT_END: break; @@ -117,6 +118,7 @@ sort_client_cmp(const void *a0, const void *b0) result = 1; break; case SORT_INDEX: + case SORT_MODIFIER: case SORT_ORDER: case SORT_END: break; @@ -167,6 +169,7 @@ sort_session_cmp(const void *a0, const void *b0) case SORT_NAME: result = strcmp(sa->name, sb->name); break; + case SORT_MODIFIER: case SORT_ORDER: case SORT_SIZE: case SORT_END: @@ -208,6 +211,7 @@ sort_pane_cmp(const void *a0, const void *b0) case SORT_NAME: result = strcmp(a->screen->title, b->screen->title); break; + case SORT_MODIFIER: case SORT_ORDER: case SORT_END: break; @@ -263,6 +267,7 @@ sort_winlink_cmp(const void *a0, const void *b0) case SORT_SIZE: result = wa->sx * wa->sy - wb->sx * wb->sy; break; + case SORT_MODIFIER: case SORT_ORDER: case SORT_END: break; @@ -276,6 +281,41 @@ sort_winlink_cmp(const void *a0, const void *b0) return (result); } +static int +sort_key_binding_cmp(const void *a0, const void *b0) +{ + struct sort_criteria *sort_crit = sort_criteria; + const struct key_binding *a = *(struct key_binding **)a0; + const struct key_binding *b = *(struct key_binding **)b0; + int result = 0; + + switch (sort_crit->order) { + case SORT_INDEX: + result = a->key - b->key; + break; + case SORT_MODIFIER: + result = (a->key & KEYC_MASK_MODIFIERS) - + (b->key & KEYC_MASK_MODIFIERS); + break; + case SORT_NAME: + result = strcasecmp(a->tablename, b->tablename) == 0; + break; + case SORT_ACTIVITY: + case SORT_CREATION: + case SORT_ORDER: + case SORT_SIZE: + case SORT_END: + break; + } + + if (result == 0) + result = strcasecmp(a->tablename, b->tablename) == 0; + + if (sort_crit->reversed) + result = -result; + return (result); +} + void sort_next_order(struct sort_criteria *sort_crit) { @@ -306,8 +346,11 @@ sort_order_from_string(const char* order) return (SORT_ACTIVITY); if (strcasecmp(order, "creation") == 0) return (SORT_CREATION); - if (strcasecmp(order, "index") == 0) + if (strcasecmp(order, "index") == 0 || + strcasecmp(order, "key") == 0) return (SORT_INDEX); + if (strcasecmp(order, "modifier") == 0) + return (SORT_MODIFIER); if (strcasecmp(order, "name") == 0 || strcasecmp(order, "title") == 0) return (SORT_NAME); @@ -328,6 +371,8 @@ sort_order_to_string(enum sort_order order) return "creation"; if (order == SORT_INDEX) return "index"; + if (order == SORT_MODIFIER) + return "modifier"; if (order == SORT_NAME) return "name"; if (order == SORT_ORDER) @@ -548,3 +593,57 @@ sort_get_winlinks_session(struct session *s, u_int *n, return (l); } + +struct key_binding ** +sort_get_key_bindings(u_int *n, struct sort_criteria *sort_crit) +{ + struct key_table *table; + struct key_binding *bd; + u_int i = 0; + static struct key_binding **l = NULL; + static u_int lsz = 0; + + table = key_bindings_first_table(); + while (table != NULL) { + bd = key_bindings_first(table); + while (bd != NULL) { + if (lsz <= i) { + lsz += 100; + l = xreallocarray(l, lsz, sizeof *l); + } + l[i++] = bd; + bd = key_bindings_next(table, bd); + } + table = key_bindings_next_table(table); + } + + sort_qsort(l, i, sizeof *l, sort_key_binding_cmp, sort_crit); + *n = i; + + return (l); +} + +struct key_binding ** +sort_get_key_bindings_table(struct key_table *table, u_int *n, + struct sort_criteria *sort_crit) +{ + struct key_binding *bd; + u_int i = 0; + static struct key_binding **l = NULL; + static u_int lsz = 0; + + bd = key_bindings_first(table); + while (bd != NULL) { + if (lsz <= i) { + lsz += 100; + l = xreallocarray(l, lsz, sizeof *l); + } + l[i++] = bd; + bd = key_bindings_next(table, bd); + } + + sort_qsort(l, i, sizeof *l, sort_key_binding_cmp, sort_crit); + *n = i; + + return (l); +} diff --git a/tmux.1 b/tmux.1 index d70f884a..68892668 100644 --- a/tmux.1 +++ b/tmux.1 @@ -2247,6 +2247,18 @@ Same as .Ic scroll-down but also exit copy mode if the cursor reaches the bottom. .It Xo +.It Xo +.Ic scroll-exit-on +.Xc +Turn on exiting copy mode when scrolling to the end of the buffer. +.It Xo +.Ic scroll-exit-off +.Xc +Turn off exiting copy mode when scrolling to the end of the buffer. +.It Xo +.Ic scroll-exit-toggle +.Xc +Toggle exiting copy mode when scrolling to the end of the buffer. .Ic scroll-middle (vi: z) .Xc @@ -3827,24 +3839,45 @@ To view the default bindings and possible commands, see the command. .Tg lsk .It Xo Ic list-keys -.Op Fl 1aN +.Op Fl 1aNr +.Op Fl F Ar format +.Op Fl O Ar sort-order .Op Fl P Ar prefix-string .Op Fl T Ar key-table .Op Ar key .Xc .D1 Pq alias: Ic lsk List key bindings. -There are two forms: the default lists keys as +.Fl F +specifies the format of each line. +See the +.Sx FORMATS +section. +.Fl T +specifies a +.Ar key-table +to list from. +.Fl 1 +lists only the first matching key. +.Fl O +specifies the sort order: one of +.Ql key , +.Ql modifier , +.Ql name +(table name). +.Fl r +reverses the sort order. +.Pp +If no +.Ar format +is given, there are two forms: the default lists keys as .Ic bind-key commands; .Fl N lists only keys with attached notes and shows only the key and note for each key. .Pp -With the default form, all key tables are listed by default. -.Fl T -lists only keys in -.Ar key-table . +With the default form, all key tables are listed unless specified otherwise. .Pp With the .Fl N @@ -3857,9 +3890,7 @@ key tables are listed by default; also lists only keys in .Ar key-table . .Fl P -specifies a prefix to print before each key and -.Fl 1 -lists only the first matching key. +specifies a prefix to print before each key. .Fl a lists the command for keys that do not have a note rather than skipping them. .Tg send @@ -6236,6 +6267,15 @@ The following variables are available, where appropriate: .It Li "host" Ta "#H" Ta "Hostname of local host" .It Li "host_short" Ta "#h" Ta "Hostname of local host (no domain name)" .It Li "insert_flag" Ta "" Ta "Pane insert flag" +.It Li "key_string" Ta "" Ta "String representation of the key binding" +.It Li "key_repeat" Ta "" Ta "1 if key binding is repeatable" +.It Li "key_note" Ta "" Ta "Note of the key binding" +.It Li "key_prefix" Ta "" Ta "Global key prefix" +.It Li "key_table" Ta "" Ta "Table name of the key binding" +.It Li "key_command" Ta "" Ta "Command of the key binding" +.It Li "key_has_repeat" Ta "" Ta "1 if list contain a repeatable key" +.It Li "key_string_width" Ta "" Ta "Maximum key_string width in list" +.It Li "key_table_width" Ta "" Ta "Maximum key_table width in list" .It Li "keypad_cursor_flag" Ta "" Ta "Pane keypad cursor flag" .It Li "keypad_flag" Ta "" Ta "Pane keypad flag" .It Li "last_window_index" Ta "" Ta "Index of last window in session" @@ -7411,7 +7451,7 @@ is the contents are read from stdin. .Tg pasteb .It Xo Ic paste-buffer -.Op Fl dpr +.Op Fl dprS .Op Fl b Ar buffer-name .Op Fl s Ar separator .Op Fl t Ar target-pane @@ -7419,9 +7459,14 @@ the contents are read from stdin. .D1 Pq alias: Ic pasteb Insert the contents of a paste buffer into the specified pane. If not specified, paste into the current one. +By default, control characters are sanitized with +.Xr vis 3 ; +.Fl S +disables this. With .Fl d , also delete the paste buffer. +.Pp When output, any linefeed (LF) characters in the paste buffer are replaced with a separator, by default carriage return (CR). A custom separator may be specified using the diff --git a/tmux.h b/tmux.h index 53c6d7ba..5f30cd0d 100644 --- a/tmux.h +++ b/tmux.h @@ -2146,6 +2146,7 @@ struct key_binding { key_code key; struct cmd_list *cmdlist; const char *note; + const char *tablename; int flags; #define KEY_BINDING_REPEAT 0x1 @@ -2284,6 +2285,7 @@ enum sort_order { SORT_ACTIVITY, SORT_CREATION, SORT_INDEX, + SORT_MODIFIER, SORT_NAME, SORT_ORDER, SORT_SIZE, @@ -2382,6 +2384,10 @@ struct window_pane **sort_get_panes_window(struct window *, u_int *, struct winlink **sort_get_winlinks(u_int *, struct sort_criteria *); struct winlink **sort_get_winlinks_session(struct session *, u_int *, struct sort_criteria *); +struct key_binding **sort_get_key_bindings(u_int *, + struct sort_criteria *); +struct key_binding **sort_get_key_bindings_table(struct key_table *, + u_int *, struct sort_criteria *); /* format.c */ #define FORMAT_STATUS 0x1 @@ -2894,6 +2900,7 @@ void key_bindings_reset(const char *, key_code); void key_bindings_remove_table(const char *); void key_bindings_reset_table(const char *); void key_bindings_init(void); +int key_bindings_has_repeat(struct key_binding **, u_int); struct cmdq_item *key_bindings_dispatch(struct key_binding *, struct cmdq_item *, struct client *, struct key_event *, struct cmd_find_state *); @@ -3615,9 +3622,9 @@ void utf8_copy(struct utf8_data *, const struct utf8_data *); enum utf8_state utf8_open(struct utf8_data *, u_char); enum utf8_state utf8_append(struct utf8_data *, u_char); int utf8_isvalid(const char *); -int utf8_strvis(char *, const char *, size_t, int); -int utf8_stravis(char **, const char *, int); -int utf8_stravisx(char **, const char *, size_t, int); +size_t utf8_strvis(char *, const char *, size_t, int); +size_t utf8_stravis(char **, const char *, int); +size_t utf8_stravisx(char **, const char *, size_t, int); char *utf8_sanitize(const char *); size_t utf8_strlen(const struct utf8_data *); u_int utf8_strwidth(const struct utf8_data *, ssize_t); diff --git a/utf8.c b/utf8.c index 95b7ceb3..951dc528 100644 --- a/utf8.c +++ b/utf8.c @@ -652,7 +652,7 @@ utf8_append(struct utf8_data *ud, u_char ch) * bytes available for each character from src (for \abc or UTF-8) plus space * for \0. */ -int +size_t utf8_strvis(char *dst, const char *src, size_t len, int flag) { struct utf8_data ud; @@ -690,11 +690,11 @@ utf8_strvis(char *dst, const char *src, size_t len, int flag) } /* Same as utf8_strvis but allocate the buffer. */ -int +size_t utf8_stravis(char **dst, const char *src, int flag) { char *buf; - int len; + size_t len; buf = xreallocarray(NULL, 4, strlen(src) + 1); len = utf8_strvis(buf, src, strlen(src), flag); @@ -704,11 +704,11 @@ utf8_stravis(char **dst, const char *src, int flag) } /* Same as utf8_strvis but allocate the buffer. */ -int +size_t utf8_stravisx(char **dst, const char *src, size_t srclen, int flag) { char *buf; - int len; + size_t len; buf = xreallocarray(NULL, 4, srclen + 1); len = utf8_strvis(buf, src, srclen, flag); diff --git a/window-copy.c b/window-copy.c index c4f10b37..902d8314 100644 --- a/window-copy.c +++ b/window-copy.c @@ -2127,6 +2127,36 @@ window_copy_cmd_rectangle_toggle(struct window_copy_cmd_state *cs) return (WINDOW_COPY_CMD_NOTHING); } +static enum window_copy_cmd_action +window_copy_cmd_scroll_exit_on(struct window_copy_cmd_state *cs) +{ + struct window_copy_mode_data *data = cs->wme->data; + + data->scroll_exit = 1; + + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_scroll_exit_off(struct window_copy_cmd_state *cs) +{ + struct window_copy_mode_data *data = cs->wme->data; + + data->scroll_exit = 0; + + return (WINDOW_COPY_CMD_NOTHING); +} + +static enum window_copy_cmd_action +window_copy_cmd_scroll_exit_toggle(struct window_copy_cmd_state *cs) +{ + struct window_copy_mode_data *data = cs->wme->data; + + data->scroll_exit = !data->scroll_exit; + + return (WINDOW_COPY_CMD_NOTHING); +} + static enum window_copy_cmd_action window_copy_cmd_scroll_down(struct window_copy_cmd_state *cs) { @@ -2702,16 +2732,20 @@ window_copy_cmd_refresh_from_pane(struct window_copy_cmd_state *cs) struct window_mode_entry *wme = cs->wme; struct window_pane *wp = wme->swp; struct window_copy_mode_data *data = wme->data; + u_int oy_from_top; if (data->viewmode) return (WINDOW_COPY_CMD_NOTHING); + oy_from_top = screen_hsize(data->backing) - data->oy; screen_free(data->backing); free(data->backing); data->backing = window_copy_clone_screen(&wp->base, &data->screen, NULL, NULL, wme->swp != wme->wp); - if (data->oy > screen_hsize(data->backing)) { + if (oy_from_top <= screen_hsize(data->backing)) + data->oy = screen_hsize(data->backing) - oy_from_top; + else { data->cy = 0; data->oy = screen_hsize(data->backing); } @@ -3073,6 +3107,21 @@ static const struct { .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, .f = window_copy_cmd_scroll_down_and_cancel }, + { .command = "scroll-exit-on", + .args = { "", 0, 0, NULL }, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_scroll_exit_on + }, + { .command = "scroll-exit-off", + .args = { "", 0, 0, NULL }, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_scroll_exit_off + }, + { .command = "scroll-exit-toggle", + .args = { "", 0, 0, NULL }, + .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS, + .f = window_copy_cmd_scroll_exit_toggle + }, { .command = "scroll-middle", .args = { "", 0, 0, NULL }, .clear = WINDOW_COPY_CMD_CLEAR_ALWAYS,