diff --git a/Makefile b/Makefile index 14cf2fd2..8a3aaa4b 100644 --- a/Makefile +++ b/Makefile @@ -114,6 +114,7 @@ SRCS= alerts.c \ spawn.c \ status.c \ style.c \ + sort.c \ tmux.c \ tty-acs.c \ tty-draw.c \ diff --git a/cmd-list-buffers.c b/cmd-list-buffers.c index 8b12f0b3..6e37a980 100644 --- a/cmd-list-buffers.c +++ b/cmd-list-buffers.c @@ -36,8 +36,8 @@ const struct cmd_entry cmd_list_buffers_entry = { .name = "list-buffers", .alias = "lsb", - .args = { "F:f:", 0, 0, NULL }, - .usage = "[-F format] [-f filter]", + .args = { "F:f:O:r", 0, 0, NULL }, + .usage = "[-F format] [-f filter] [-O order]", .flags = CMD_AFTERHOOK, .exec = cmd_list_buffers_exec @@ -46,21 +46,26 @@ const struct cmd_entry cmd_list_buffers_entry = { static enum cmd_retval cmd_list_buffers_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct paste_buffer *pb; - struct format_tree *ft; - const char *template, *filter; - char *line, *expanded; - int flag; + struct args *args = cmd_get_args(self); + struct paste_buffer **l; + struct format_tree *ft; + const char *template, *filter; + char *line, *expanded; + int flag; + u_int i, n; + struct sort_criteria sort_crit; if ((template = args_get(args, 'F')) == NULL) template = LIST_BUFFERS_TEMPLATE; filter = args_get(args, 'f'); - pb = NULL; - while ((pb = paste_walk(pb)) != NULL) { + sort_crit.order = sort_order_from_string(args_get(args, 'O')); + sort_crit.reversed = args_has(args, 'r'); + + l = sort_get_buffers(&n, &sort_crit); + for (i = 0; i < n; i++) { ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); - format_defaults_paste_buffer(ft, pb); + format_defaults_paste_buffer(ft, l[i]); if (filter != NULL) { expanded = format_expand(ft, filter); diff --git a/cmd-list-clients.c b/cmd-list-clients.c index da7541bc..68cf043c 100644 --- a/cmd-list-clients.c +++ b/cmd-list-clients.c @@ -41,8 +41,8 @@ const struct cmd_entry cmd_list_clients_entry = { .name = "list-clients", .alias = "lsc", - .args = { "F:f:t:", 0, 0, NULL }, - .usage = "[-F format] [-f filter] " CMD_TARGET_SESSION_USAGE, + .args = { "F:f:O:rt:", 0, 0, NULL }, + .usage = "[-F format] [-f filter] [-O order]" CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, @@ -53,15 +53,16 @@ const struct cmd_entry cmd_list_clients_entry = { static enum cmd_retval cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct cmd_find_state *target = cmdq_get_target(item); - struct client *c; - struct session *s; - struct format_tree *ft; - const char *template, *filter; - u_int idx; - char *line, *expanded; - int flag; + struct args *args = cmd_get_args(self); + struct cmd_find_state *target = cmdq_get_target(item); + struct client **l; + struct session *s; + struct format_tree *ft; + const char *template, *filter; + u_int i, n; + char *line, *expanded; + int flag; + struct sort_criteria sort_crit; if (args_has(args, 't')) s = target->s; @@ -72,14 +73,17 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) template = LIST_CLIENTS_TEMPLATE; filter = args_get(args, 'f'); - idx = 0; - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL || (s != NULL && s != c->session)) + sort_crit.order = sort_order_from_string(args_get(args, 'O')); + sort_crit.reversed = args_has(args, 'r'); + + l = sort_get_clients(&n, &sort_crit); + for (i = 0; i < n; i++) { + if (l[i]->session == NULL || (s != NULL && s != l[i]->session)) continue; ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); - format_add(ft, "line", "%u", idx); - format_defaults(ft, c, NULL, NULL, NULL); + format_add(ft, "line", "%u", i); + format_defaults(ft, l[i], NULL, NULL, NULL); if (filter != NULL) { expanded = format_expand(ft, filter); @@ -94,8 +98,6 @@ cmd_list_clients_exec(struct cmd *self, struct cmdq_item *item) } format_free(ft); - - idx++; } return (CMD_RETURN_NORMAL); diff --git a/cmd-list-panes.c b/cmd-list-panes.c index a29a4032..fe7e1a4f 100644 --- a/cmd-list-panes.c +++ b/cmd-list-panes.c @@ -38,8 +38,9 @@ const struct cmd_entry cmd_list_panes_entry = { .name = "list-panes", .alias = "lsp", - .args = { "asF:f:t:", 0, 0, NULL }, - .usage = "[-as] [-F format] [-f filter] " CMD_TARGET_WINDOW_USAGE, + .args = { "aF:f:O:rst:", 0, 0, NULL }, + .usage = "[-asr] [-F format] [-f filter] [-O order]" + CMD_TARGET_WINDOW_USAGE, .target = { 't', CMD_FIND_WINDOW, 0 }, @@ -89,12 +90,13 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, struct cmdq_item *item, int type) { struct args *args = cmd_get_args(self); - struct window_pane *wp; - u_int n; + struct window_pane *wp, **l; + u_int i, n; struct format_tree *ft; const char *template, *filter; char *line, *expanded; int flag; + struct sort_criteria sort_crit; template = args_get(args, 'F'); if (template == NULL) { @@ -124,8 +126,12 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, } filter = args_get(args, 'f'); - n = 0; - TAILQ_FOREACH(wp, &wl->window->panes, entry) { + sort_crit.order = sort_order_from_string(args_get(args, 'O')); + sort_crit.reversed = args_has(args, 'r'); + + l = sort_get_panes_window(wl->window, &n, &sort_crit); + for (i = 0; i < n; i++) { + wp = l[i]; ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, wp); @@ -143,6 +149,5 @@ cmd_list_panes_window(struct cmd *self, struct session *s, struct winlink *wl, } format_free(ft); - n++; } } diff --git a/cmd-list-sessions.c b/cmd-list-sessions.c index e448524e..89fefb5c 100644 --- a/cmd-list-sessions.c +++ b/cmd-list-sessions.c @@ -42,8 +42,8 @@ const struct cmd_entry cmd_list_sessions_entry = { .name = "list-sessions", .alias = "ls", - .args = { "F:f:", 0, 0, NULL }, - .usage = "[-F format] [-f filter]", + .args = { "F:f:O:r", 0, 0, NULL }, + .usage = "[-r] [-F format] [-f filter] [-O order]", .flags = CMD_AFTERHOOK, .exec = cmd_list_sessions_exec @@ -52,23 +52,27 @@ const struct cmd_entry cmd_list_sessions_entry = { static enum cmd_retval cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item) { - struct args *args = cmd_get_args(self); - struct session *s; - u_int n; - struct format_tree *ft; - const char *template, *filter; - char *line, *expanded; - int flag; + struct args *args = cmd_get_args(self); + struct session **l; + u_int n, i; + struct format_tree *ft; + const char *template, *filter; + char *line, *expanded; + int flag; + struct sort_criteria sort_crit; if ((template = args_get(args, 'F')) == NULL) template = LIST_SESSIONS_TEMPLATE; filter = args_get(args, 'f'); - n = 0; - RB_FOREACH(s, sessions, &sessions) { + sort_crit.order = sort_order_from_string(args_get(args, 'O')); + sort_crit.reversed = args_has(args, 'r'); + + l = sort_get_sessions(&n, &sort_crit); + for (i = 0; i < n; i++) { ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); - format_add(ft, "line", "%u", n); - format_defaults(ft, NULL, s, NULL, NULL); + format_add(ft, "line", "%u", i); + format_defaults(ft, NULL, l[i], NULL, NULL); if (filter != NULL) { expanded = format_expand(ft, filter); @@ -83,7 +87,6 @@ cmd_list_sessions_exec(struct cmd *self, struct cmdq_item *item) } format_free(ft); - n++; } return (CMD_RETURN_NORMAL); diff --git a/cmd-list-windows.c b/cmd-list-windows.c index 035471d4..a7438de0 100644 --- a/cmd-list-windows.c +++ b/cmd-list-windows.c @@ -41,16 +41,13 @@ static enum cmd_retval cmd_list_windows_exec(struct cmd *, struct cmdq_item *); -static void cmd_list_windows_server(struct cmd *, struct cmdq_item *); -static void cmd_list_windows_session(struct cmd *, struct session *, - struct cmdq_item *, int); - const struct cmd_entry cmd_list_windows_entry = { .name = "list-windows", .alias = "lsw", - .args = { "F:f:at:", 0, 0, NULL }, - .usage = "[-a] [-F format] [-f filter] " CMD_TARGET_SESSION_USAGE, + .args = { "aF:f:O:rt:", 0, 0, NULL }, + .usage = "[-ar] [-F format] [-f filter] [-O order]" + CMD_TARGET_SESSION_USAGE, .target = { 't', CMD_FIND_SESSION, 0 }, @@ -63,51 +60,34 @@ cmd_list_windows_exec(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); struct cmd_find_state *target = cmdq_get_target(item); - - if (args_has(args, 'a')) - cmd_list_windows_server(self, item); - else - cmd_list_windows_session(self, target->s, item, 0); - - return (CMD_RETURN_NORMAL); -} - -static void -cmd_list_windows_server(struct cmd *self, struct cmdq_item *item) -{ - struct session *s; - - RB_FOREACH(s, sessions, &sessions) - cmd_list_windows_session(self, s, item, 1); -} - -static void -cmd_list_windows_session(struct cmd *self, struct session *s, - struct cmdq_item *item, int type) -{ - struct args *args = cmd_get_args(self); - struct winlink *wl; - u_int n; + struct winlink *wl, **l; + struct session *s; + u_int i, n; struct format_tree *ft; const char *template, *filter; char *line, *expanded; int flag; + struct sort_criteria sort_crit; template = args_get(args, 'F'); - if (template == NULL) { - switch (type) { - case 0: - template = LIST_WINDOWS_TEMPLATE; - break; - case 1: - template = LIST_WINDOWS_WITH_SESSION_TEMPLATE; - break; - } - } filter = args_get(args, 'f'); - n = 0; - RB_FOREACH(wl, winlinks, &s->windows) { + sort_crit.order = sort_order_from_string(args_get(args, 'O')); + sort_crit.reversed = args_has(args, 'r'); + + if (args_has(args, 'a')) { + l = sort_get_winlinks(&n, &sort_crit); + if (template == NULL) + template = LIST_WINDOWS_WITH_SESSION_TEMPLATE; + } else { + l = sort_get_winlinks_session(target->s, &n, &sort_crit); + if (template == NULL) + template = LIST_WINDOWS_TEMPLATE; + } + + for (i = 0; i < n; i++) { + wl = l[i]; + s = wl->session; ft = format_create(cmdq_get_client(item), item, FORMAT_NONE, 0); format_add(ft, "line", "%u", n); format_defaults(ft, NULL, s, wl, NULL); @@ -125,6 +105,7 @@ cmd_list_windows_session(struct cmd *self, struct session *s, } format_free(ft); - n++; } + + return (CMD_RETURN_NORMAL); } diff --git a/cmd-switch-client.c b/cmd-switch-client.c index 0ff8cf99..9317d4f3 100644 --- a/cmd-switch-client.c +++ b/cmd-switch-client.c @@ -34,9 +34,9 @@ const struct cmd_entry cmd_switch_client_entry = { .name = "switch-client", .alias = "switchc", - .args = { "lc:EFnpt:rT:Z", 0, 0, NULL }, + .args = { "c:EFlnO:pt:rT:Z", 0, 0, NULL }, .usage = "[-ElnprZ] [-c target-client] [-t target-session] " - "[-T key-table]", + "[-T key-table] [-O order]", /* -t is special */ @@ -60,6 +60,7 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) struct window_pane *wp; const char *tablename; struct key_table *table; + struct sort_criteria sort_crit; if (tflag != NULL && (tflag[strcspn(tflag, ":.%")] != '\0' || strcmp(tflag, "=") == 0)) { @@ -95,13 +96,18 @@ cmd_switch_client_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } + sort_crit.order = sort_order_from_string(args_get(args, 'O')); + sort_crit.reversed = args_has(args, 'r'); + if (args_has(args, 'n')) { - if ((s = session_next_session(tc->session)) == NULL) { + s = session_next_session(tc->session, &sort_crit); + if (s == NULL) { cmdq_error(item, "can't find next session"); return (CMD_RETURN_ERROR); } } else if (args_has(args, 'p')) { - if ((s = session_previous_session(tc->session)) == NULL) { + s = session_previous_session(tc->session, &sort_crit); + if (s == NULL) { cmdq_error(item, "can't find previous session"); return (CMD_RETURN_ERROR); } diff --git a/format.c b/format.c index 6628ef20..5804869d 100644 --- a/format.c +++ b/format.c @@ -132,17 +132,7 @@ enum format_type { FORMAT_TYPE_PANE }; -/* Format loop sort type. */ -enum format_loop_sort_type { - FORMAT_LOOP_BY_INDEX, - FORMAT_LOOP_BY_NAME, - FORMAT_LOOP_BY_TIME, -}; - -static struct format_loop_sort_criteria { - enum format_loop_sort_type field; - int reversed; -} format_loop_sort_criteria; +static struct sort_criteria sort_crit; struct format_tree { enum format_type type; @@ -4388,44 +4378,11 @@ format_session_name(struct format_expand_state *es, const char *fmt) return (xstrdup("0")); } -static int -format_cmp_session(const void *a0, const void *b0) -{ - struct format_loop_sort_criteria *sc = &format_loop_sort_criteria; - const struct session *const *a = a0; - const struct session *const *b = b0; - const struct session *sa = *a; - const struct session *sb = *b; - int result = 0; - - switch (sc->field) { - case FORMAT_LOOP_BY_INDEX: - result = sa->id - sb->id; - break; - case FORMAT_LOOP_BY_TIME: - if (timercmp(&sa->activity_time, &sb->activity_time, >)) { - result = -1; - break; - } - if (timercmp(&sa->activity_time, &sb->activity_time, <)) { - result = 1; - break; - } - /* FALLTHROUGH */ - case FORMAT_LOOP_BY_NAME: - result = strcmp(sa->name, sb->name); - break; - } - - if (sc->reversed) - result = -result; - return (result); -} - /* Loop over sessions. */ static char * format_loop_sessions(struct format_expand_state *es, const char *fmt) { + struct sort_criteria *sc = &sort_crit; struct format_tree *ft = es->ft; struct client *c = ft->client; struct cmdq_item *item = ft->item; @@ -4433,30 +4390,18 @@ format_loop_sessions(struct format_expand_state *es, const char *fmt) struct format_expand_state next; char *all, *active, *use, *expanded, *value; size_t valuelen; - struct session *s; + struct session *s, **l; int i, n, last = 0; - static struct session **l = NULL; - static int lsz = 0; if (format_choose(es, fmt, &all, &active, 0) != 0) { all = xstrdup(fmt); active = NULL; } - n = 0; - RB_FOREACH(s, sessions, &sessions) { - if (lsz <= n) { - lsz += 100; - l = xreallocarray(l, lsz, sizeof *l); - } - l[n++] = s; - } - - qsort(l, n, sizeof *l, format_cmp_session); - value = xcalloc(1, 1); valuelen = 1; + l = sort_get_sessions(&n, sc); for (i = 0; i < n; i++) { s = l[i]; format_log(es, "session loop: $%u", s->id); @@ -4509,44 +4454,11 @@ format_window_name(struct format_expand_state *es, const char *fmt) return (xstrdup("0")); } -static int -format_cmp_window(const void *a0, const void *b0) -{ - struct format_loop_sort_criteria *sc = &format_loop_sort_criteria; - const struct winlink *const *a = a0; - const struct winlink *const *b = b0; - const struct window *wa = (*a)->window; - const struct window *wb = (*b)->window; - int result = 0; - - switch (sc->field) { - case FORMAT_LOOP_BY_INDEX: - break; - case FORMAT_LOOP_BY_TIME: - if (timercmp(&wa->activity_time, &wb->activity_time, >)) { - result = -1; - break; - } - if (timercmp(&wa->activity_time, &wb->activity_time, <)) { - result = 1; - break; - } - /* FALLTHROUGH */ - case FORMAT_LOOP_BY_NAME: - result = strcmp(wa->name, wb->name); - break; - } - - if (sc->reversed) - result = -result; - return (result); -} - /* Loop over windows. */ static char * format_loop_windows(struct format_expand_state *es, const char *fmt) { - struct format_loop_sort_criteria *sc = &format_loop_sort_criteria; + struct sort_criteria *sc = &sort_crit; struct format_tree *ft = es->ft; struct client *c = ft->client; struct cmdq_item *item = ft->item; @@ -4554,11 +4466,9 @@ format_loop_windows(struct format_expand_state *es, const char *fmt) struct format_expand_state next; char *all, *active, *use, *expanded, *value; size_t valuelen; - struct winlink *wl; + struct winlink *wl, **l; struct window *w; int i, n, last = 0; - static struct winlink **l = NULL; - static int lsz = 0; if (ft->s == NULL) { format_log(es, "window loop but no session"); @@ -4570,31 +4480,10 @@ format_loop_windows(struct format_expand_state *es, const char *fmt) active = NULL; } - n = 0; - RB_FOREACH(wl, winlinks, &ft->s->windows) { - if (lsz <= n) { - lsz += 100; - l = xreallocarray(l, lsz, sizeof *l); - } - l[n++] = wl; - } - - if (sc->field != FORMAT_LOOP_BY_INDEX) - qsort(l, n, sizeof *l, format_cmp_window); - else { - /* Use order in the tree as index order. */ - if (sc->reversed) { - for (i = 0; i < n / 2; i++) { - wl = l[i]; - l[i] = l[n - 1 - i]; - l[n - 1 - i] = wl; - } - } - } - value = xcalloc(1, 1); valuelen = 1; + l = sort_get_winlinks_session(ft->s, &n, sc); for (i = 0; i < n; i++) { wl = l[i]; w = wl->window; @@ -4626,27 +4515,11 @@ format_loop_windows(struct format_expand_state *es, const char *fmt) return (value); } -static int -format_cmp_pane(const void *a0, const void *b0) -{ - struct format_loop_sort_criteria *sc = &format_loop_sort_criteria; - const struct window_pane *const *a = a0; - const struct window_pane *const *b = b0; - const struct window_pane *wpa = *a; - const struct window_pane *wpb = *b; - int result = 0; - - if (sc->reversed) - result = wpb->id - wpa->id; - else - result = wpa->id - wpb->id; - return (result); -} - /* Loop over panes. */ static char * format_loop_panes(struct format_expand_state *es, const char *fmt) { + struct sort_criteria *sc = &sort_crit; struct format_tree *ft = es->ft; struct client *c = ft->client; struct cmdq_item *item = ft->item; @@ -4654,10 +4527,8 @@ format_loop_panes(struct format_expand_state *es, const char *fmt) struct format_expand_state next; char *all, *active, *use, *expanded, *value; size_t valuelen; - struct window_pane *wp; + struct window_pane *wp, **l; int i, n, last = 0; - static struct window_pane **l = NULL; - static int lsz = 0; if (ft->w == NULL) { format_log(es, "pane loop but no window"); @@ -4669,20 +4540,10 @@ format_loop_panes(struct format_expand_state *es, const char *fmt) active = NULL; } - n = 0; - TAILQ_FOREACH(wp, &ft->w->panes, entry) { - if (lsz <= n) { - lsz += 100; - l = xreallocarray(l, lsz, sizeof *l); - } - l[n++] = wp; - } - - qsort(l, n, sizeof *l, format_cmp_pane); - value = xcalloc(1, 1); valuelen = 1; + l = sort_get_panes_window(ft->w, &n, sc); for (i = 0; i < n; i++) { wp = l[i]; format_log(es, "pane loop: %%%u", wp->id); @@ -4713,80 +4574,24 @@ format_loop_panes(struct format_expand_state *es, const char *fmt) return (value); } -static int -format_cmp_client(const void *a0, const void *b0) -{ - struct format_loop_sort_criteria *sc = &format_loop_sort_criteria; - const struct client *const *a = a0; - const struct client *const *b = b0; - const struct client *ca = *a; - const struct client *cb = *b; - int result = 0; - - switch (sc->field) { - case FORMAT_LOOP_BY_INDEX: - break; - case FORMAT_LOOP_BY_TIME: - if (timercmp(&ca->activity_time, &cb->activity_time, >)) { - result = -1; - break; - } - if (timercmp(&ca->activity_time, &cb->activity_time, <)) { - result = 1; - break; - } - /* FALLTHROUGH */ - case FORMAT_LOOP_BY_NAME: - result = strcmp(ca->name, cb->name); - break; - } - - if (sc->reversed) - result = -result; - return (result); -} - /* Loop over clients. */ static char * format_loop_clients(struct format_expand_state *es, const char *fmt) { - struct format_loop_sort_criteria *sc = &format_loop_sort_criteria; + struct sort_criteria *sc = &sort_crit; struct format_tree *ft = es->ft; - struct client *c; + struct client *c, **l; struct cmdq_item *item = ft->item; struct format_tree *nft; struct format_expand_state next; char *expanded, *value; size_t valuelen; int i, n, last = 0; - static struct client **l = NULL; - static int lsz = 0; value = xcalloc(1, 1); valuelen = 1; - n = 0; - TAILQ_FOREACH(c, &clients, entry) { - if (lsz <= n) { - lsz += 100; - l = xreallocarray(l, lsz, sizeof *l); - } - l[n++] = c; - } - - if (sc->field != FORMAT_LOOP_BY_INDEX) - qsort(l, n, sizeof *l, format_cmp_client); - else { - /* Use order in the list as index order. */ - if (sc->reversed) { - for (i = 0; i < n / 2; i++) { - c = l[i]; - l[i] = l[n - 1 - i]; - l[n - 1 - i] = c; - } - } - } - + l = sort_get_clients(&n, sc); for (i = 0; i < n; i++) { c = l[i]; format_log(es, "client loop: %s", c->name); @@ -4956,7 +4761,7 @@ static int format_replace(struct format_expand_state *es, const char *key, size_t keylen, char **buf, size_t *len, size_t *off) { - struct format_loop_sort_criteria *sc = &format_loop_sort_criteria; + struct sort_criteria *sc = &sort_crit; struct format_tree *ft = es->ft; struct window_pane *wp = ft->wp; const char *errstr, *copy, *cp, *cp2; @@ -4973,6 +4778,10 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, u_int i, count, nsub = 0, nrep; struct format_expand_state next; + /* Set sorting defaults. */ + sc->order = SORT_ORDER; + sc->reversed = 0; + /* Make a copy of the key. */ copy = copy0 = xstrndup(key, keylen); @@ -5083,18 +4892,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'S': modifiers |= FORMAT_SESSIONS; if (fm->argc < 1) { - sc->field = FORMAT_LOOP_BY_INDEX; + sc->order= SORT_INDEX; sc->reversed = 0; break; } if (strchr(fm->argv[0], 'i') != NULL) - sc->field = FORMAT_LOOP_BY_INDEX; + sc->order = SORT_INDEX; else if (strchr(fm->argv[0], 'n') != NULL) - sc->field = FORMAT_LOOP_BY_NAME; + sc->order = SORT_NAME; else if (strchr(fm->argv[0], 't') != NULL) - sc->field = FORMAT_LOOP_BY_TIME; + sc->order = SORT_ACTIVITY; else - sc->field = FORMAT_LOOP_BY_INDEX; + sc->order = SORT_INDEX; if (strchr(fm->argv[0], 'r') != NULL) sc->reversed = 1; else @@ -5103,18 +4912,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'W': modifiers |= FORMAT_WINDOWS; if (fm->argc < 1) { - sc->field = FORMAT_LOOP_BY_INDEX; + sc->order = SORT_ORDER; sc->reversed = 0; break; } if (strchr(fm->argv[0], 'i') != NULL) - sc->field = FORMAT_LOOP_BY_INDEX; + sc->order = SORT_ORDER; else if (strchr(fm->argv[0], 'n') != NULL) - sc->field = FORMAT_LOOP_BY_NAME; + sc->order = SORT_NAME; else if (strchr(fm->argv[0], 't') != NULL) - sc->field = FORMAT_LOOP_BY_TIME; + sc->order = SORT_ACTIVITY; else - sc->field = FORMAT_LOOP_BY_INDEX; + sc->order = SORT_ORDER; if (strchr(fm->argv[0], 'r') != NULL) sc->reversed = 1; else @@ -5122,6 +4931,7 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, break; case 'P': modifiers |= FORMAT_PANES; + sc->order = SORT_CREATION; if (fm->argc < 1) { sc->reversed = 0; break; @@ -5134,18 +4944,18 @@ format_replace(struct format_expand_state *es, const char *key, size_t keylen, case 'L': modifiers |= FORMAT_CLIENTS; if (fm->argc < 1) { - sc->field = FORMAT_LOOP_BY_INDEX; + sc->order = SORT_ORDER; sc->reversed = 0; break; } if (strchr(fm->argv[0], 'i') != NULL) - sc->field = FORMAT_LOOP_BY_INDEX; + sc->order = SORT_ORDER; else if (strchr(fm->argv[0], 'n') != NULL) - sc->field = FORMAT_LOOP_BY_NAME; + sc->order = SORT_NAME; else if (strchr(fm->argv[0], 't') != NULL) - sc->field = FORMAT_LOOP_BY_TIME; + sc->order = SORT_ACTIVITY; else - sc->field = FORMAT_LOOP_BY_INDEX; + sc->order = SORT_ORDER; if (strchr(fm->argv[0], 'r') != NULL) sc->reversed = 1; else diff --git a/mode-tree.c b/mode-tree.c index a3620b4f..d76ff93d 100644 --- a/mode-tree.c +++ b/mode-tree.c @@ -48,9 +48,7 @@ struct mode_tree_data { void *modedata; const struct menu_item *menu; - const char **sort_list; - u_int sort_size; - struct mode_tree_sort_criteria sort_crit; + struct sort_criteria sort_crit; mode_tree_build_cb buildcb; mode_tree_draw_cb drawcb; @@ -59,6 +57,7 @@ struct mode_tree_data { mode_tree_height_cb heightcb; mode_tree_key_cb keycb; mode_tree_swap_cb swapcb; + mode_tree_sort_cb sortcb; struct mode_tree_list children; struct mode_tree_list saved; @@ -324,7 +323,7 @@ mode_tree_swap(struct mode_tree_data *mtd, int direction) return; if (mtd->swapcb(mtd->line_list[mtd->current].item->itemdata, - mtd->line_list[swap_with].item->itemdata)) { + mtd->line_list[swap_with].item->itemdata, &mtd->sort_crit)) { mtd->current = swap_with; mode_tree_build(mtd); } @@ -382,7 +381,7 @@ mode_tree_expand(struct mode_tree_data *mtd, uint64_t tag) u_int found; if (!mode_tree_get_tag(mtd, tag, &found)) - return; + return; if (!mtd->line_list[found].item->expanded) { mtd->line_list[found].item->expanded = 1; mode_tree_build(mtd); @@ -454,12 +453,10 @@ 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, mode_tree_key_cb keycb, - mode_tree_swap_cb swapcb, void *modedata, const struct menu_item *menu, - const char **sort_list, u_int sort_size, struct screen **s) + mode_tree_swap_cb swapcb, mode_tree_sort_cb sortcb, void *modedata, + const struct menu_item *menu, struct screen **s) { struct mode_tree_data *mtd; - const char *sort; - u_int i; mtd = xcalloc(1, sizeof *mtd); mtd->references = 1; @@ -468,9 +465,6 @@ mode_tree_start(struct window_pane *wp, struct args *args, mtd->modedata = modedata; mtd->menu = menu; - mtd->sort_list = sort_list; - mtd->sort_size = sort_size; - if (args_has(args, 'N') > 1) mtd->preview = MODE_TREE_PREVIEW_BIG; else if (args_has(args, 'N')) @@ -478,13 +472,7 @@ mode_tree_start(struct window_pane *wp, struct args *args, else mtd->preview = MODE_TREE_PREVIEW_NORMAL; - sort = args_get(args, 'O'); - if (sort != NULL) { - for (i = 0; i < sort_size; i++) { - if (strcasecmp(sort, sort_list[i]) == 0) - mtd->sort_crit.field = i; - } - } + mtd->sort_crit.order = sort_order_from_string(args_get(args, 'O')); mtd->sort_crit.reversed = args_has(args, 'r'); if (args_has(args, 'f')) @@ -499,6 +487,7 @@ mode_tree_start(struct window_pane *wp, struct args *args, mtd->heightcb = heightcb; mtd->keycb = keycb; mtd->swapcb = swapcb; + mtd->sortcb = sortcb; TAILQ_INIT(&mtd->children); @@ -566,6 +555,8 @@ mode_tree_build(struct mode_tree_data *mtd) TAILQ_CONCAT(&mtd->saved, &mtd->children, entry); TAILQ_INIT(&mtd->children); + if (mtd->sortcb != NULL) + mtd->sortcb(&mtd->sort_crit); mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter); mtd->no_matches = TAILQ_EMPTY(&mtd->children); if (mtd->no_matches) @@ -851,9 +842,9 @@ mode_tree_draw(struct mode_tree_data *mtd) screen_write_cursormove(&ctx, 0, h, 0); screen_write_box(&ctx, w, sy - h, BOX_LINES_DEFAULT, NULL, NULL); - if (mtd->sort_list != NULL) { + if (mtd->sort_crit.order_seq != NULL) { xasprintf(&text, " %s (sort: %s%s)", mti->name, - mtd->sort_list[mtd->sort_crit.field], + sort_order_to_string(mtd->sort_crit.order), mtd->sort_crit.reversed ? ", reversed" : ""); } else xasprintf(&text, " %s", mti->name); @@ -1287,9 +1278,7 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key, } break; case 'O': - mtd->sort_crit.field++; - if (mtd->sort_crit.field >= mtd->sort_size) - mtd->sort_crit.field = 0; + sort_next_order(&mtd->sort_crit); mode_tree_build(mtd); break; case 'r': diff --git a/paste.c b/paste.c index 559ace19..a35bc4b3 100644 --- a/paste.c +++ b/paste.c @@ -30,19 +30,6 @@ * string! */ -struct paste_buffer { - char *data; - size_t size; - - char *name; - time_t created; - int automatic; - u_int order; - - RB_ENTRY(paste_buffer) name_entry; - RB_ENTRY(paste_buffer) time_entry; -}; - static u_int paste_next_index; static u_int paste_next_order; static u_int paste_num_automatic; diff --git a/server-fn.c b/server-fn.c index 72c0f8f8..fc20fce6 100644 --- a/server-fn.c +++ b/server-fn.c @@ -438,9 +438,9 @@ server_destroy_session(struct session *s) else if (detach_on_destroy == 2) s_new = server_find_session(s, server_newer_detached_session); else if (detach_on_destroy == 3) - s_new = session_previous_session(s); + s_new = session_previous_session(s, NULL); else if (detach_on_destroy == 4) - s_new = session_next_session(s); + s_new = session_next_session(s, NULL); /* * If no suitable new session was found above, then look for any diff --git a/session.c b/session.c index afa48637..06f83a46 100644 --- a/session.c +++ b/session.c @@ -295,36 +295,48 @@ session_update_activity(struct session *s, struct timeval *from) /* Find the next usable session. */ struct session * -session_next_session(struct session *s) +session_next_session(struct session *s, struct sort_criteria *sort_crit) { - struct session *s2; + struct session **l; + u_int n, i; if (RB_EMPTY(&sessions) || !session_alive(s)) return (NULL); - s2 = RB_NEXT(sessions, &sessions, s); - if (s2 == NULL) - s2 = RB_MIN(sessions, &sessions); - if (s2 == s) - return (NULL); - return (s2); + l = sort_get_sessions(&n, sort_crit); + for (i = 0; i < n; i++) { + if (l[i] == s) + break; + } + if (i == n) + fatalx("session %s not found in sorted list", s->name); + i++; + if (i == n) + i = 0; + return (l[i]); } /* Find the previous usable session. */ struct session * -session_previous_session(struct session *s) +session_previous_session(struct session *s, struct sort_criteria *sort_crit) { - struct session *s2; + struct session **l; + u_int n, i; if (RB_EMPTY(&sessions) || !session_alive(s)) return (NULL); - s2 = RB_PREV(sessions, &sessions, s); - if (s2 == NULL) - s2 = RB_MAX(sessions, &sessions); - if (s2 == s) - return (NULL); - return (s2); + l = sort_get_sessions(&n, sort_crit); + for (i = 0; i < n; i++) { + if (l[i] == s) + break; + } + if (i == n) + fatalx("session %s not found in sorted list", s->name); + if (i == 0) + i = n; + i--; + return (l[i]); } /* Attach a window to a session. */ diff --git a/sort.c b/sort.c new file mode 100644 index 00000000..a1d85891 --- /dev/null +++ b/sort.c @@ -0,0 +1,536 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2026 Dane Jensen + * + * 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" + +static struct sort_criteria *sort_criteria; + +static void +sort_qsort(void *l, u_int len, u_int size, int (*cmp)(const void *, const void *), + struct sort_criteria *sort_crit) +{ + u_int i; + void *tmp, **ll; + + if (sort_crit->order == SORT_END) + return; + + if (sort_crit->order == SORT_ORDER) { + if (sort_crit->reversed) { + ll = l; + for (i = 0; i < len / 2; i++) { + tmp = ll[i]; + ll[i] = ll[len - 1 - i]; + ll[len - 1 - i] = tmp; + } + } + } else { + sort_criteria = sort_crit; + qsort(l, len, size, cmp); + } +} + +static int +sort_buffer_cmp(const void *a0, const void *b0) +{ + struct sort_criteria *sort_crit = sort_criteria; + const struct paste_buffer *const *a = a0; + const struct paste_buffer *const *b = b0; + const struct paste_buffer *pa = *a; + const struct paste_buffer *pb = *b; + int result = 0; + + switch (sort_crit->order) { + case SORT_NAME: + result = strcmp(pa->name, pb->name); + break; + case SORT_CREATION: + result = pa->order - pb->order; + break; + case SORT_SIZE: + result = pa->size - pb->size; + break; + case SORT_ACTIVITY: + case SORT_INDEX: + case SORT_ORDER: + case SORT_END: + break; + } + + if (result == 0) + result = strcmp(pa->name, pb->name); + + if (sort_crit->reversed) + result = -result; + return (result); +} + +static int +sort_client_cmp(const void *a0, const void *b0) +{ + struct sort_criteria *sort_crit = sort_criteria; + const struct client *const *a = a0; + const struct client *const *b = b0; + const struct client *ca = *a; + const struct client *cb = *b; + int result = 0; + + switch (sort_crit->order) { + case SORT_NAME: + result = strcmp(ca->name, cb->name); + break; + case SORT_SIZE: + result = ca->tty.sx - cb->tty.sx; + if (result == 0) + result = ca->tty.sy - cb->tty.sy; + break; + case SORT_CREATION: + if (timercmp(&ca->creation_time, &cb->creation_time, >)) + result = 1; + else if (timercmp(&ca->creation_time, &cb->creation_time, <)) + result = -1; + break; + case SORT_ACTIVITY: + if (timercmp(&ca->activity_time, &cb->activity_time, >)) + result = -1; + else if (timercmp(&ca->activity_time, &cb->activity_time, <)) + result = 1; + break; + case SORT_INDEX: + case SORT_ORDER: + case SORT_END: + break; + } + + if (result == 0) + result = strcmp(ca->name, cb->name); + + if (sort_crit->reversed) + result = -result; + return (result); +} + +static int +sort_session_cmp(const void *a0, const void *b0) +{ + struct sort_criteria *sort_crit = sort_criteria; + const struct session *const *a = a0; + const struct session *const *b = b0; + const struct session *sa = *a; + const struct session *sb = *b; + int result = 0; + + switch (sort_crit->order) { + case SORT_INDEX: + result = sa->id - sb->id; + break; + case SORT_CREATION: + if (timercmp(&sa->creation_time, &sb->creation_time, >)) { + result = 1; + break; + } + if (timercmp(&sa->creation_time, &sb->creation_time, <)) { + result = -1; + break; + } + break; + case SORT_ACTIVITY: + if (timercmp(&sa->activity_time, &sb->activity_time, >)) { + result = -1; + break; + } + if (timercmp(&sa->activity_time, &sb->activity_time, <)) { + result = 1; + break; + } + break; + case SORT_NAME: + result = strcmp(sa->name, sb->name); + break; + case SORT_ORDER: + case SORT_SIZE: + case SORT_END: + break; + } + + if (result == 0) + result = strcmp(sa->name, sb->name); + + if (sort_crit->reversed) + result = -result; + return (result); +} + +static int +sort_pane_cmp(const void *a0, const void *b0) +{ + struct sort_criteria *sort_crit = sort_criteria; + struct window_pane *a = *(struct window_pane **)a0; + struct window_pane *b = *(struct window_pane **)b0; + int result = 0; + u_int ai, bi; + + switch (sort_crit->order) { + case SORT_ACTIVITY: + result = a->active_point - b->active_point; + break; + case SORT_CREATION: + result = a->id - b->id; + break; + case SORT_INDEX: + case SORT_NAME: + case SORT_ORDER: + case SORT_SIZE: + case SORT_END: + break; + } + if (result == 0) { + /* + * Panes don't have names, so use number order for any other + * sort field. + */ + window_pane_index(a, &ai); + window_pane_index(b, &bi); + result = ai - bi; + } + + if (sort_crit->reversed) + result = -result; + return (result); +} + +static int +sort_winlink_cmp(const void *a0, const void *b0) +{ + struct sort_criteria *sort_crit = sort_criteria; + const struct winlink *const *a = a0; + const struct winlink *const *b = b0; + const struct winlink *wla = *a; + const struct winlink *wlb = *b; + struct window *wa = wla->window; + struct window *wb = wlb->window; + int result = 0; + + switch (sort_crit->order) { + case SORT_INDEX: + result = wla->idx - wlb->idx; + break; + case SORT_ACTIVITY: + if (timercmp(&wa->activity_time, &wb->activity_time, >)) { + result = -1; + break; + } + if (timercmp(&wa->activity_time, &wb->activity_time, <)) { + result = 1; + break; + } + break; + case SORT_NAME: + result = strcmp(wa->name, wb->name); + break; + case SORT_CREATION: + case SORT_ORDER: + case SORT_SIZE: + case SORT_END: + break; + } + + if (result == 0) + result = strcmp(wa->name, wb->name); + + if (sort_crit->reversed) + result = -result; + return (result); +} + +void +sort_next_order(struct sort_criteria *sort_crit) +{ + u_int i; + + if (sort_crit->order_seq == NULL) + return; + for (i = 0; sort_crit->order_seq[i] != SORT_END; i++) { + if (sort_crit->order == sort_crit->order_seq[i]) + break; + } + + if (sort_crit->order_seq[i] == SORT_END) + i = 0; + else { + i++; + if (sort_crit->order_seq[i] == SORT_END) + i = 0; + } + sort_crit->order = sort_crit->order_seq[i]; +} + +enum sort_order +sort_order_from_string(const char* order) +{ + if (order != NULL) { + if (strcasecmp(order, "activity") == 0) + return (SORT_ACTIVITY); + if (strcasecmp(order, "creation") == 0) + return (SORT_CREATION); + if (strcasecmp(order, "index") == 0) + return (SORT_INDEX); + if (strcasecmp(order, "name") == 0) + return (SORT_NAME); + if (strcasecmp(order, "order") == 0) + return (SORT_ORDER); + if (strcasecmp(order, "size") == 0) + return (SORT_SIZE); + } + return (SORT_END); +} + +const char * +sort_order_to_string(enum sort_order order) +{ + if (order == SORT_ACTIVITY) + return "activity"; + if (order == SORT_CREATION) + return "creation"; + if (order == SORT_INDEX) + return "index"; + if (order == SORT_NAME) + return "name"; + if (order == SORT_ORDER) + return "order"; + if (order == SORT_SIZE) + return "size"; + return (NULL); +} + +int +sort_would_window_tree_swap(struct sort_criteria *sort_crit, + struct winlink *wla, struct winlink *wlb) +{ + if (sort_crit->order == SORT_INDEX) + return (0); + sort_criteria = sort_crit; + return (sort_winlink_cmp(&wla, &wlb) != 0); +} + +struct paste_buffer ** +sort_get_buffers(u_int *n, struct sort_criteria *sort_crit) +{ + struct paste_buffer *pb = NULL; + u_int i; + static struct paste_buffer **l = NULL; + static u_int lsz = 0; + + i = 0; + while ((pb = paste_walk(pb)) != NULL) { + if (lsz <= i) { + lsz += 100; + l = xreallocarray(l, lsz, sizeof *l); + } + l[i++] = pb; + } + + sort_qsort(l, i, sizeof *l, sort_buffer_cmp, sort_crit); + *n = i; + + return (l); +} + +struct client ** +sort_get_clients(u_int *n, struct sort_criteria *sort_crit) +{ + struct client *c; + u_int i; + static struct client **l = NULL; + static u_int lsz = 0; + + i = 0; + TAILQ_FOREACH(c, &clients, entry) { + if (lsz <= i) { + lsz += 100; + l = xreallocarray(l, lsz, sizeof *l); + } + l[i++] = c; + } + + sort_qsort(l, i, sizeof *l, sort_client_cmp, sort_crit); + *n = i; + + return (l); +} + +struct session ** +sort_get_sessions(u_int *n, struct sort_criteria *sort_crit) +{ + struct session *s; + u_int i; + static struct session **l = NULL; + static u_int lsz = 0; + + i = 0; + RB_FOREACH(s, sessions, &sessions) { + if (lsz <= i) { + lsz += 100; + l = xreallocarray(l, lsz, sizeof *l); + } + l[i++] = s; + } + + sort_qsort(l, i, sizeof *l, sort_session_cmp, sort_crit); + *n = i; + + return (l); +} + +struct window_pane ** +sort_get_panes(u_int *n, struct sort_criteria *sort_crit) +{ + struct session *s; + struct winlink *wl; + struct window *w; + struct window_pane *wp; + u_int i; + static struct window_pane **l = NULL; + static u_int lsz = 0; + + i = 0; + RB_FOREACH(s, sessions, &sessions) { + RB_FOREACH(wl, winlinks, &s->windows) { + w = wl->window; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (lsz <= i) { + lsz += 100; + l = xreallocarray(l, lsz, sizeof *l); + } + l[i++] = wp; + } + } + } + + sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit); + *n = i; + + return (l); +} + +struct window_pane ** +sort_get_panes_session(struct session *s, u_int *n, + struct sort_criteria *sort_crit) +{ + struct winlink *wl = NULL; + struct window *w = NULL; + struct window_pane *wp = NULL; + u_int i; + static struct window_pane **l = NULL; + static u_int lsz = 0; + + i = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + TAILQ_FOREACH(wp, &w->panes, entry) { + if (lsz <= i) { + lsz += 100; + l = xreallocarray(l, lsz, sizeof *l); + } + l[i++] = wp; + } + } + + sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit); + *n = i; + + return (l); +} + +struct window_pane ** +sort_get_panes_window(struct window *w, u_int *n, + struct sort_criteria *sort_crit) +{ + struct window_pane *wp; + u_int i; + static struct window_pane **l = NULL; + static u_int lsz = 0; + + i = 0; + TAILQ_FOREACH(wp, &w->panes, entry) { + if (lsz <= i) { + lsz += 100; + l = xreallocarray(l, lsz, sizeof *l); + } + l[i++] = wp; + } + + sort_qsort(l, i, sizeof *l, sort_pane_cmp, sort_crit); + *n = i; + + return (l); +} + +struct winlink ** +sort_get_winlinks(u_int *n, struct sort_criteria *sort_crit) +{ + struct session *s; + struct winlink *wl; + u_int i; + static struct winlink **l = NULL; + static u_int lsz = 0; + + i = 0; + RB_FOREACH(s, sessions, &sessions) { + RB_FOREACH(wl, winlinks, &s->windows) { + if (lsz <= i) { + lsz += 100; + l = xreallocarray(l, lsz, sizeof *l); + } + l[i++] = wl; + } + } + + sort_qsort(l, i, sizeof *l, sort_winlink_cmp, sort_crit); + *n = i; + + return (l); +} + +struct winlink ** +sort_get_winlinks_session(struct session *s, u_int *n, + struct sort_criteria *sort_crit) +{ + struct winlink *wl; + u_int i; + static struct winlink **l = NULL; + static u_int lsz = 0; + + i = 0; + RB_FOREACH(wl, winlinks, &s->windows) { + if (lsz <= i) { + lsz += 100; + l = xreallocarray(l, lsz, sizeof *l); + } + l[i++] = wl; + } + + sort_qsort(l, i, sizeof *l, sort_winlink_cmp, sort_crit); + *n = i; + + return (l); +} diff --git a/tmux.1 b/tmux.1 index f58511f2..48751888 100644 --- a/tmux.1 +++ b/tmux.1 @@ -1183,8 +1183,10 @@ flag clears alerts (bell, activity, or silence) in all windows linked to the session. .Tg lsc .It Xo Ic list-clients +.Op Fl r .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl O Ar sort-order .Op Fl t Ar target-session .Xc .D1 Pq alias: Ic lsc @@ -1197,6 +1199,16 @@ Only clients for which the filter is true are shown. See the .Sx FORMATS section. +.Fl O +specifies the sort order: one of +.Ql name , +.Ql size , +.Ql creation +(time), or +.Ql activity +(time). +.Fl r +reverses the sort order. If .Ar target-session is specified, list only clients connected to that session. @@ -1212,8 +1224,10 @@ or - if omitted - of all commands supported by .Nm . .Tg ls .It Xo Ic list-sessions +.Op Fl r .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl O Ar sort-order .Xc .D1 Pq alias: Ic ls List all sessions managed by the server. @@ -1225,6 +1239,16 @@ Only sessions for which the filter is true are shown. See the .Sx FORMATS section. +.Fl O +specifies the sort order: one of +.Ql index , +.Ql name , +.Ql creation +(time), or +.Ql activity +(time). +.Fl r +reverses the sort order. .Tg lockc .It Ic lock-client Op Fl t Ar target-client .D1 Pq alias: Ic lockc @@ -1621,6 +1645,7 @@ Suspend a client by sending .It Xo Ic switch-client .Op Fl ElnprZ .Op Fl c Ar target-client +.Op Fl O Ar sort-order .Op Fl t Ar target-session .Op Fl T Ar key-table .Xc @@ -1647,6 +1672,18 @@ or .Fl p is used, the client is moved to the last, next or previous session respectively. +.Fl O +may be used with +.Fl n +and +.Fl p +to specify the field to sort on: one of +.Ql name , +.Ql size , +.Ql creation +(time), or +.Ql activity +(time). .Fl r toggles the client .Ic read-only @@ -2667,7 +2704,7 @@ The following keys may be used in client mode: .It Li "z" Ta "Suspend selected client" .It Li "Z" Ta "Suspend tagged clients" .It Li "f" Ta "Enter a format to filter items" -.It Li "O" Ta "Change sort field" +.It Li "O" Ta "Change sort order" .It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" @@ -2683,12 +2720,11 @@ If is not given, "detach-client -t \[aq]%%\[aq]" is used. .Pp .Fl O -specifies the initial sort field: one of +specifies the initial sort order: one of .Ql name , .Ql size , .Ql creation -(time), -or +(time), or .Ql activity (time). .Fl r @@ -2755,7 +2791,7 @@ The following keys may be used in tree mode: .It Li "\&:" Ta "Run a command for each tagged item" .It Li "f" Ta "Enter a format to filter items" .It Li "H" Ta "Jump to the starting pane" -.It Li "O" Ta "Change sort field" +.It Li "O" Ta "Change sort order" .It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" @@ -2773,12 +2809,12 @@ If is not given, "switch-client -t \[aq]%%\[aq]" is used. .Pp .Fl O -specifies the initial sort field: one of +specifies the initial sort order: one of .Ql index , .Ql name , or -.Ql time -(activity). +.Ql activity +(time). .Fl r reverses the sort order. .Fl f @@ -3024,9 +3060,10 @@ If is given, the newly linked window is not selected. .Tg lsp .It Xo Ic list-panes -.Op Fl as +.Op Fl ars .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl O Ar sort-order .Op Fl t Ar target .Xc .D1 Pq alias: Ic lsp @@ -3051,11 +3088,21 @@ Only panes for which the filter is true are shown. See the .Sx FORMATS section. +.Fl O +specifies the sort order: one of +.Ql name , +.Ql creation +(time), or +.Ql activity +(time). +.Fl r +reverses the sort order. .Tg lsw .It Xo Ic list-windows -.Op Fl a +.Op Fl ar .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl O Ar sort-order .Op Fl t Ar target-session .Xc .D1 Pq alias: Ic lsw @@ -3072,6 +3119,15 @@ Only windows for which the filter is true are shown. See the .Sx FORMATS section. +.Fl O +specifies the sort order: one of +.Ql index , +.Ql name , +or +.Ql activity +(time). +.Fl r +reverses the sort order. .Tg movep .It Xo Ic move-pane .Op Fl bdfhv @@ -7245,7 +7301,7 @@ The following keys may be used in buffer mode: .It Li "D" Ta "Delete tagged buffers" .It Li "e" Ta "Open the buffer in an editor" .It Li "f" Ta "Enter a format to filter items" -.It Li "O" Ta "Change sort field" +.It Li "O" Ta "Change sort order" .It Li "r" Ta "Reverse sort order" .It Li "v" Ta "Toggle preview" .It Li "q" Ta "Exit mode" @@ -7261,10 +7317,10 @@ If is not given, "paste-buffer -p -b \[aq]%%\[aq]" is used. .Pp .Fl O -specifies the initial sort field: one of -.Ql time -(creation), -.Ql name +specifies the initial sort order: one of +.Ql creation +(time), +.Ql name , or .Ql size . .Fl r @@ -7297,8 +7353,10 @@ Delete the buffer named or the most recently added automatically named buffer if not specified. .Tg lsb .It Xo Ic list-buffers +.Op Fl r .Op Fl F Ar format .Op Fl f Ar filter +.Op Fl O Ar sort-order .Xc .D1 Pq alias: Ic lsb List the global buffers. @@ -7310,6 +7368,15 @@ Only buffers for which the filter is true are shown. See the .Sx FORMATS section. +.Fl O +specifies the sort order: one of +.Ql name , +.Ql size , +or +.Ql creation +(time). +.Fl r +reverses the sort order. .It Xo Ic load-buffer .Op Fl w .Op Fl b Ar buffer-name diff --git a/tmux.h b/tmux.h index 545b002f..be4632c0 100644 --- a/tmux.h +++ b/tmux.h @@ -2227,10 +2227,36 @@ struct spawn_context { #define SPAWN_ZOOM 0x80 }; -/* Mode tree sort order. */ -struct mode_tree_sort_criteria { - u_int field; - int reversed; +/* Paste buffer. */ +struct paste_buffer { + char *data; + size_t size; + + char *name; + time_t created; + int automatic; + u_int order; + + RB_ENTRY(paste_buffer) name_entry; + RB_ENTRY(paste_buffer) time_entry; +}; + +/* Sort orders. */ +enum sort_order { + SORT_ACTIVITY, + SORT_CREATION, + SORT_INDEX, + SORT_NAME, + SORT_ORDER, + SORT_SIZE, + SORT_END, +}; + +/* Sort criteria. */ +struct sort_criteria { + enum sort_order order; + int reversed; + enum sort_order *order_seq; /* available sort orders */ }; /* tmux.c */ @@ -2286,7 +2312,6 @@ void cfg_print_causes(struct cmdq_item *); void cfg_show_causes(struct session *); /* paste.c */ -struct paste_buffer; const char *paste_buffer_name(struct paste_buffer *); u_int paste_buffer_order(struct paste_buffer *); time_t paste_buffer_created(struct paste_buffer *); @@ -2302,6 +2327,24 @@ int paste_set(char *, size_t, const char *, char **); void paste_replace(struct paste_buffer *, char *, size_t); char *paste_make_sample(struct paste_buffer *); +/* sort.c */ +void sort_next_order(struct sort_criteria *); +enum sort_order sort_order_from_string(const char *); +const char *sort_order_to_string(enum sort_order); +int sort_would_window_tree_swap(struct sort_criteria *, + struct winlink *, struct winlink *); +struct paste_buffer **sort_get_buffers(u_int *, struct sort_criteria *); +struct client **sort_get_clients(u_int *, struct sort_criteria *); +struct session **sort_get_sessions(u_int *, struct sort_criteria *); +struct window_pane **sort_get_panes(u_int *, struct sort_criteria *); +struct window_pane **sort_get_panes_session(struct session *, u_int *, + struct sort_criteria *); +struct window_pane **sort_get_panes_window(struct window *, u_int *, + struct sort_criteria *); +struct winlink **sort_get_winlinks(u_int *, struct sort_criteria *); +struct winlink **sort_get_winlinks_session(struct session *, u_int *, + struct sort_criteria *); + /* format.c */ #define FORMAT_STATUS 0x1 #define FORMAT_FORCE 0x2 @@ -3347,7 +3390,7 @@ u_int layout_set_next(struct window *); u_int layout_set_previous(struct window *); /* mode-tree.c */ -typedef void (*mode_tree_build_cb)(void *, struct mode_tree_sort_criteria *, +typedef void (*mode_tree_build_cb)(void *, struct sort_criteria *, uint64_t *, const char *); typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *, u_int, u_int); @@ -3355,7 +3398,8 @@ typedef int (*mode_tree_search_cb)(void *, void *, const char *, int); 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 int (*mode_tree_swap_cb)(void *, void *); +typedef int (*mode_tree_swap_cb)(void *, void *, struct sort_criteria *); +typedef void (*mode_tree_sort_cb)(struct sort_criteria *); 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 *); @@ -3371,8 +3415,8 @@ int 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, mode_tree_key_cb, - mode_tree_swap_cb, void *, const struct menu_item *, const char **, - u_int, struct screen **); + mode_tree_swap_cb, mode_tree_sort_cb, void *, + const struct menu_item *, struct screen **); void mode_tree_zoom(struct mode_tree_data *, struct args *); void mode_tree_build(struct mode_tree_data *); void mode_tree_free(struct mode_tree_data *); @@ -3482,8 +3526,9 @@ void session_add_ref(struct session *, const char *); void session_remove_ref(struct session *, const char *); char *session_check_name(const char *); void session_update_activity(struct session *, struct timeval *); -struct session *session_next_session(struct session *); -struct session *session_previous_session(struct session *); +struct session *session_next_session(struct session *, struct sort_criteria *); +struct session *session_previous_session(struct session *, + struct sort_criteria *); struct winlink *session_attach(struct session *, struct window *, int, char **); int session_detach(struct session *, struct winlink *); diff --git a/window-buffer.c b/window-buffer.c index 782a7854..cbd13b7d 100644 --- a/window-buffer.c +++ b/window-buffer.c @@ -76,18 +76,6 @@ const struct window_mode window_buffer_mode = { .key = window_buffer_key, }; -enum window_buffer_sort_type { - WINDOW_BUFFER_BY_TIME, - WINDOW_BUFFER_BY_NAME, - WINDOW_BUFFER_BY_SIZE, -}; -static const char *window_buffer_sort_list[] = { - "time", - "name", - "size" -}; -static struct mode_tree_sort_criteria *window_buffer_sort; - struct window_buffer_itemdata { const char *name; u_int order; @@ -113,6 +101,13 @@ struct window_buffer_editdata { struct paste_buffer *pb; }; +static enum sort_order window_buffer_order_seq[] = { + SORT_CREATION, + SORT_NAME, + SORT_SIZE, + SORT_END, +}; + static struct window_buffer_itemdata * window_buffer_add_item(struct window_buffer_modedata *data) { @@ -131,35 +126,14 @@ window_buffer_free_item(struct window_buffer_itemdata *item) free(item); } -static int -window_buffer_cmp(const void *a0, const void *b0) -{ - const struct window_buffer_itemdata *const *a = a0; - const struct window_buffer_itemdata *const *b = b0; - int result = 0; - - if (window_buffer_sort->field == WINDOW_BUFFER_BY_TIME) - result = (*b)->order - (*a)->order; - else if (window_buffer_sort->field == WINDOW_BUFFER_BY_SIZE) - result = (*b)->size - (*a)->size; - - /* Use WINDOW_BUFFER_BY_NAME as default order and tie breaker. */ - if (result == 0) - result = strcmp((*a)->name, (*b)->name); - - if (window_buffer_sort->reversed) - result = -result; - return (result); -} - static void -window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, +window_buffer_build(void *modedata, struct sort_criteria *sort_crit, __unused uint64_t *tag, const char *filter) { struct window_buffer_modedata *data = modedata; struct window_buffer_itemdata *item; - u_int i; - struct paste_buffer *pb = NULL; + u_int i, n; + struct paste_buffer *pb, **l; char *text, *cp; struct format_tree *ft; struct session *s = NULL; @@ -172,17 +146,14 @@ window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, data->item_list = NULL; data->item_size = 0; - while ((pb = paste_walk(pb)) != NULL) { + l = sort_get_buffers(&n, sort_crit); + for (i = 0; i < n; i++) { item = window_buffer_add_item(data); - item->name = xstrdup(paste_buffer_name(pb)); - paste_buffer_data(pb, &item->size); - item->order = paste_buffer_order(pb); + item->name = xstrdup(paste_buffer_name(l[i])); + paste_buffer_data(l[i], &item->size); + item->order = paste_buffer_order(l[i]); } - window_buffer_sort = sort_crit; - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_buffer_cmp); - if (cmd_find_valid_state(&data->fs)) { s = data->fs.s; wl = data->fs.wl; @@ -216,7 +187,6 @@ window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, format_free(ft); } - } static void @@ -351,6 +321,14 @@ window_buffer_get_key(void *modedata, void *itemdata, u_int line) return (key); } +static void +window_buffer_sort(struct sort_criteria *sort_crit) +{ + sort_crit->order_seq = window_buffer_order_seq; + if (sort_crit->order == SORT_END) + sort_crit->order = sort_crit->order_seq[0]; +} + static struct screen * window_buffer_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) @@ -378,8 +356,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, - window_buffer_get_key, NULL, data, window_buffer_menu_items, - window_buffer_sort_list, nitems(window_buffer_sort_list), &s); + window_buffer_get_key, NULL, window_buffer_sort, data, + window_buffer_menu_items, &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); diff --git a/window-client.c b/window-client.c index a7735250..9f2d5dce 100644 --- a/window-client.c +++ b/window-client.c @@ -71,20 +71,6 @@ const struct window_mode window_client_mode = { .key = window_client_key, }; -enum window_client_sort_type { - WINDOW_CLIENT_BY_NAME, - WINDOW_CLIENT_BY_SIZE, - WINDOW_CLIENT_BY_CREATION_TIME, - WINDOW_CLIENT_BY_ACTIVITY_TIME, -}; -static const char *window_client_sort_list[] = { - "name", - "size", - "creation", - "activity" -}; -static struct mode_tree_sort_criteria *window_client_sort; - struct window_client_itemdata { struct client *c; }; @@ -101,6 +87,14 @@ struct window_client_modedata { u_int item_size; }; +static enum sort_order window_client_order_seq[] = { + SORT_NAME, + SORT_SIZE, + SORT_CREATION, + SORT_ACTIVITY, + SORT_END, +}; + static struct window_client_itemdata * window_client_add_item(struct window_client_modedata *data) { @@ -119,54 +113,14 @@ window_client_free_item(struct window_client_itemdata *item) free(item); } -static int -window_client_cmp(const void *a0, const void *b0) -{ - const struct window_client_itemdata *const *a = a0; - const struct window_client_itemdata *const *b = b0; - const struct window_client_itemdata *itema = *a; - const struct window_client_itemdata *itemb = *b; - struct client *ca = itema->c; - struct client *cb = itemb->c; - int result = 0; - - switch (window_client_sort->field) { - case WINDOW_CLIENT_BY_SIZE: - result = ca->tty.sx - cb->tty.sx; - if (result == 0) - result = ca->tty.sy - cb->tty.sy; - break; - case WINDOW_CLIENT_BY_CREATION_TIME: - if (timercmp(&ca->creation_time, &cb->creation_time, >)) - result = -1; - else if (timercmp(&ca->creation_time, &cb->creation_time, <)) - result = 1; - break; - case WINDOW_CLIENT_BY_ACTIVITY_TIME: - if (timercmp(&ca->activity_time, &cb->activity_time, >)) - result = -1; - else if (timercmp(&ca->activity_time, &cb->activity_time, <)) - result = 1; - break; - } - - /* Use WINDOW_CLIENT_BY_NAME as default order and tie breaker. */ - if (result == 0) - result = strcmp(ca->name, cb->name); - - if (window_client_sort->reversed) - result = -result; - return (result); -} - static void -window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, +window_client_build(void *modedata, struct sort_criteria *sort_crit, __unused uint64_t *tag, const char *filter) { struct window_client_modedata *data = modedata; struct window_client_itemdata *item; - u_int i; - struct client *c; + u_int i, n; + struct client *c, **l; char *text, *cp; for (i = 0; i < data->item_size; i++) @@ -175,20 +129,18 @@ window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, data->item_list = NULL; data->item_size = 0; - TAILQ_FOREACH(c, &clients, entry) { - if (c->session == NULL || (c->flags & CLIENT_UNATTACHEDFLAGS)) + l = sort_get_clients(&n, sort_crit); + for (i = 0; i < n; i++) { + if (l[i]->session == NULL || + (l[i]->flags & CLIENT_UNATTACHEDFLAGS)) continue; item = window_client_add_item(data); - item->c = c; + item->c = l[i]; - c->references++; + l[i]->references++; } - window_client_sort = sort_crit; - qsort(data->item_list, data->item_size, sizeof *data->item_list, - window_client_cmp); - for (i = 0; i < data->item_size; i++) { item = data->item_list[i]; c = item->c; @@ -280,6 +232,14 @@ window_client_get_key(void *modedata, void *itemdata, u_int line) return (key); } +static void +window_client_sort(struct sort_criteria *sort_crit) +{ + sort_crit->order_seq = window_client_order_seq; + if (sort_crit->order == SORT_END) + sort_crit->order = sort_crit->order_seq[0]; +} + static struct screen * window_client_init(struct window_mode_entry *wme, __unused struct cmd_find_state *fs, struct args *args) @@ -306,8 +266,8 @@ window_client_init(struct window_mode_entry *wme, data->data = mode_tree_start(wp, args, window_client_build, window_client_draw, NULL, window_client_menu, NULL, - window_client_get_key, NULL, data, window_client_menu_items, - window_client_sort_list, nitems(window_client_sort_list), &s); + window_client_get_key, NULL, window_client_sort, data, + window_client_menu_items, &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); diff --git a/window-customize.c b/window-customize.c index f30becfe..0887aa5c 100644 --- a/window-customize.c +++ b/window-customize.c @@ -525,7 +525,7 @@ window_customize_build_keys(struct window_customize_modedata *data, static void window_customize_build(void *modedata, - __unused struct mode_tree_sort_criteria *sort_crit, __unused uint64_t *tag, + __unused struct sort_criteria *sort_crit, __unused uint64_t *tag, const char *filter) { struct window_customize_modedata *data = modedata; @@ -891,8 +891,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, NULL, NULL, data, - window_customize_menu_items, NULL, 0, &s); + window_customize_height, NULL, NULL, NULL, data, + window_customize_menu_items, &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data); diff --git a/window-tree.c b/window-tree.c index 0a2460f1..3d4e5b5c 100644 --- a/window-tree.c +++ b/window-tree.c @@ -89,18 +89,6 @@ const struct window_mode window_tree_mode = { .key = window_tree_key, }; -enum window_tree_sort_type { - WINDOW_TREE_BY_INDEX, - WINDOW_TREE_BY_NAME, - WINDOW_TREE_BY_TIME, -}; -static const char *window_tree_sort_list[] = { - "index", - "name", - "time" -}; -static struct mode_tree_sort_criteria *window_tree_sort; - enum window_tree_type { WINDOW_TREE_NONE, WINDOW_TREE_SESSION, @@ -144,6 +132,13 @@ struct window_tree_modedata { u_int each; }; +static enum sort_order window_tree_order_seq[] = { + SORT_INDEX, + SORT_NAME, + SORT_ACTIVITY, + SORT_END, +}; + static void window_tree_pull_item(struct window_tree_itemdata *item, struct session **sp, struct winlink **wlp, struct window_pane **wp) @@ -196,98 +191,6 @@ window_tree_free_item(struct window_tree_itemdata *item) free(item); } -static int -window_tree_cmp_session(const void *a0, const void *b0) -{ - const struct session *const *a = a0; - const struct session *const *b = b0; - const struct session *sa = *a; - const struct session *sb = *b; - int result = 0; - - switch (window_tree_sort->field) { - case WINDOW_TREE_BY_INDEX: - result = sa->id - sb->id; - break; - case WINDOW_TREE_BY_TIME: - if (timercmp(&sa->activity_time, &sb->activity_time, >)) { - result = -1; - break; - } - if (timercmp(&sa->activity_time, &sb->activity_time, <)) { - result = 1; - break; - } - /* FALLTHROUGH */ - case WINDOW_TREE_BY_NAME: - result = strcmp(sa->name, sb->name); - break; - } - - if (window_tree_sort->reversed) - result = -result; - return (result); -} - -static int -window_tree_cmp_window(const void *a0, const void *b0) -{ - const struct winlink *const *a = a0; - const struct winlink *const *b = b0; - const struct winlink *wla = *a; - const struct winlink *wlb = *b; - struct window *wa = wla->window; - struct window *wb = wlb->window; - int result = 0; - - switch (window_tree_sort->field) { - case WINDOW_TREE_BY_INDEX: - result = wla->idx - wlb->idx; - break; - case WINDOW_TREE_BY_TIME: - if (timercmp(&wa->activity_time, &wb->activity_time, >)) { - result = -1; - break; - } - if (timercmp(&wa->activity_time, &wb->activity_time, <)) { - result = 1; - break; - } - /* FALLTHROUGH */ - case WINDOW_TREE_BY_NAME: - result = strcmp(wa->name, wb->name); - break; - } - - if (window_tree_sort->reversed) - result = -result; - return (result); -} - -static int -window_tree_cmp_pane(const void *a0, const void *b0) -{ - struct window_pane **a = (struct window_pane **)a0; - struct window_pane **b = (struct window_pane **)b0; - int result; - u_int ai, bi; - - if (window_tree_sort->field == WINDOW_TREE_BY_TIME) - result = (*a)->active_point - (*b)->active_point; - else { - /* - * Panes don't have names, so use number order for any other - * sort field. - */ - window_pane_index(*a, &ai); - window_pane_index(*b, &bi); - result = ai - bi; - } - if (window_tree_sort->reversed) - result = -result; - return (result); -} - static void window_tree_build_pane(struct session *s, struct winlink *wl, struct window_pane *wp, void *modedata, struct mode_tree_item *parent) @@ -339,7 +242,7 @@ window_tree_filter_pane(struct session *s, struct winlink *wl, static int window_tree_build_window(struct session *s, struct winlink *wl, - void *modedata, struct mode_tree_sort_criteria *sort_crit, + void *modedata, struct sort_criteria *sort_crit, struct mode_tree_item *parent, const char *filter) { struct window_tree_modedata *data = modedata; @@ -381,24 +284,13 @@ window_tree_build_window(struct session *s, struct winlink *wl, goto empty; } - l = NULL; - n = 0; - - TAILQ_FOREACH(wp, &wl->window->panes, entry) { - if (!window_tree_filter_pane(s, wl, wp, filter)) - continue; - l = xreallocarray(l, n + 1, sizeof *l); - l[n++] = wp; - } + l = sort_get_panes_window(wl->window, &n, sort_crit); if (n == 0) goto empty; - - window_tree_sort = sort_crit; - qsort(l, n, sizeof *l, window_tree_cmp_pane); - - for (i = 0; i < n; i++) - window_tree_build_pane(s, wl, l[i], modedata, mti); - free(l); + for (i = 0; i < n; i++) { + if (window_tree_filter_pane(s, wl, l[i], filter)) + window_tree_build_pane(s, wl, l[i], modedata, mti); + } return (1); empty: @@ -410,7 +302,7 @@ empty: static void window_tree_build_session(struct session *s, void *modedata, - struct mode_tree_sort_criteria *sort_crit, const char *filter) + struct sort_criteria *sort_crit, const char *filter) { struct window_tree_modedata *data = modedata; struct window_tree_itemdata *item; @@ -440,15 +332,7 @@ window_tree_build_session(struct session *s, void *modedata, expanded); free(text); - l = NULL; - n = 0; - RB_FOREACH(wl, winlinks, &s->windows) { - l = xreallocarray(l, n + 1, sizeof *l); - l[n++] = wl; - } - window_tree_sort = sort_crit; - qsort(l, n, sizeof *l, window_tree_cmp_window); - + l = sort_get_winlinks_session(s, &n, sort_crit); empty = 0; for (i = 0; i < n; i++) { if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti, @@ -460,11 +344,10 @@ window_tree_build_session(struct session *s, void *modedata, data->item_size--; mode_tree_remove(data->data, mti); } - free(l); } static void -window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, +window_tree_build(void *modedata, struct sort_criteria *sort_crit, uint64_t *tag, const char *filter) { struct window_tree_modedata *data = modedata; @@ -480,24 +363,19 @@ window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit, data->item_list = NULL; data->item_size = 0; - l = NULL; - n = 0; - RB_FOREACH(s, sessions, &sessions) { + l = sort_get_sessions(&n, sort_crit); + if (n == 0) + return; + s = l[n - 1]; + for (i = 0; i < n; i++) { if (data->squash_groups && (sg = session_group_contains(s)) != NULL) { if ((sg == current && s != data->fs.s) || (sg != current && s != TAILQ_FIRST(&sg->sessions))) continue; } - l = xreallocarray(l, n + 1, sizeof *l); - l[n++] = s; - } - window_tree_sort = sort_crit; - qsort(l, n, sizeof *l, window_tree_cmp_session); - - for (i = 0; i < n; i++) window_tree_build_session(l[i], modedata, sort_crit, filter); - free(l); + } switch (data->type) { case WINDOW_TREE_NONE: @@ -924,7 +802,8 @@ window_tree_get_key(void *modedata, void *itemdata, u_int line) } static int -window_tree_swap(void *cur_itemdata, void *other_itemdata) +window_tree_swap(void *cur_itemdata, void *other_itemdata, + struct sort_criteria *sort_crit) { struct window_tree_itemdata *cur = cur_itemdata; struct window_tree_itemdata *other = other_itemdata; @@ -945,14 +824,12 @@ window_tree_swap(void *cur_itemdata, void *other_itemdata) if (cur_session != other_session) return (0); - if (window_tree_sort->field != WINDOW_TREE_BY_INDEX && - window_tree_cmp_window(&cur_winlink, &other_winlink) != 0) { - /* - * Swapping indexes would not swap positions in the tree, so - * prevent swapping to avoid confusing the user. - */ + /* + * Swapping indexes would not swap positions in the tree, so prevent + * swapping to avoid confusing the user. + */ + if (sort_would_window_tree_swap(sort_crit, cur_winlink, other_winlink)) return (0); - } other_window = other_winlink->window; TAILQ_REMOVE(&other_window->winlinks, other_winlink, wentry); @@ -975,6 +852,14 @@ window_tree_swap(void *cur_itemdata, void *other_itemdata) return (1); } +static void +window_tree_sort(struct sort_criteria *sort_crit) +{ + sort_crit->order_seq = window_tree_order_seq; + if (sort_crit->order == SORT_END) + sort_crit->order = sort_crit->order_seq[0]; +} + static struct screen * window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, struct args *args) @@ -1013,8 +898,8 @@ window_tree_init(struct window_mode_entry *wme, struct cmd_find_state *fs, data->data = mode_tree_start(wp, args, window_tree_build, window_tree_draw, window_tree_search, window_tree_menu, NULL, - window_tree_get_key, window_tree_swap, data, window_tree_menu_items, - window_tree_sort_list, nitems(window_tree_sort_list), &s); + window_tree_get_key, window_tree_swap, window_tree_sort, data, + window_tree_menu_items, &s); mode_tree_zoom(data->data, args); mode_tree_build(data->data);