Add sorting (-O flag) and a custom format (-F) to list-keys, from Dane

Jensen in GitHub issue 4845.
This commit is contained in:
nicm
2026-02-24 08:20:52 +00:00
parent 9316476a73
commit 09bd686327
7 changed files with 439 additions and 313 deletions

View File

@@ -28,6 +28,7 @@ SRCS= alerts.c \
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 \

107
cmd-list-commands.c Normal file
View File

@@ -0,0 +1,107 @@
/* $OpenBSD$ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#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);
}

View File

@@ -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, &ltsz, 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);
}

View File

@@ -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);
}

101
sort.c
View File

@@ -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);
}

58
tmux.1
View File

@@ -2245,6 +2245,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
@@ -3825,24 +3837,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
@@ -3855,9 +3888,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
@@ -6234,6 +6265,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"

7
tmux.h
View File

@@ -2111,6 +2111,7 @@ struct key_binding {
key_code key;
struct cmd_list *cmdlist;
const char *note;
const char *tablename;
int flags;
#define KEY_BINDING_REPEAT 0x1
@@ -2249,6 +2250,7 @@ enum sort_order {
SORT_ACTIVITY,
SORT_CREATION,
SORT_INDEX,
SORT_MODIFIER,
SORT_NAME,
SORT_ORDER,
SORT_SIZE,
@@ -2347,6 +2349,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
@@ -2849,6 +2855,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 *);