Add a flag to reverse sort in the various choose modes, from Benjamin

Poirier in GitHub issue 1875.
This commit is contained in:
nicm 2019-08-16 11:49:12 +00:00
parent 5644d37876
commit 37583f0a69
7 changed files with 195 additions and 216 deletions

View File

@ -30,8 +30,8 @@ const struct cmd_entry cmd_choose_tree_entry = {
.name = "choose-tree",
.alias = NULL,
.args = { "F:Gf:NO:st:wZ", 0, 1 },
.usage = "[-GNsw] [-F format] [-f filter] [-O sort-order] "
.args = { "F:Gf:NO:rst:wZ", 0, 1 },
.usage = "[-GNrswZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@ -44,8 +44,8 @@ const struct cmd_entry cmd_choose_client_entry = {
.name = "choose-client",
.alias = NULL,
.args = { "F:f:NO:t:Z", 0, 1 },
.usage = "[-N] [-F format] [-f filter] [-O sort-order] "
.args = { "F:f:NO:rt:Z", 0, 1 },
.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },
@ -58,8 +58,8 @@ const struct cmd_entry cmd_choose_buffer_entry = {
.name = "choose-buffer",
.alias = NULL,
.args = { "F:f:NO:t:Z", 0, 1 },
.usage = "[-N] [-F format] [-f filter] [-O sort-order] "
.args = { "F:f:NO:rt:Z", 0, 1 },
.usage = "[-NrZ] [-F format] [-f filter] [-O sort-order] "
CMD_TARGET_PANE_USAGE " [template]",
.target = { 't', CMD_FIND_PANE, 0 },

View File

@ -39,7 +39,7 @@ struct mode_tree_data {
const char **sort_list;
u_int sort_size;
u_int sort_type;
struct mode_tree_sort_criteria sort_crit;
mode_tree_build_cb buildcb;
mode_tree_draw_cb drawcb;
@ -334,7 +334,6 @@ mode_tree_start(struct window_pane *wp, struct args *args,
mtd->sort_list = sort_list;
mtd->sort_size = sort_size;
mtd->sort_type = 0;
mtd->preview = !args_has(args, 'N');
@ -342,9 +341,10 @@ mode_tree_start(struct window_pane *wp, struct args *args,
if (sort != NULL) {
for (i = 0; i < sort_size; i++) {
if (strcasecmp(sort, sort_list[i]) == 0)
mtd->sort_type = i;
mtd->sort_crit.field = i;
}
}
mtd->sort_crit.reversed = args_has(args, 'r');
if (args_has(args, 'f'))
mtd->filter = xstrdup(args_get(args, 'f'));
@ -392,10 +392,10 @@ mode_tree_build(struct mode_tree_data *mtd)
TAILQ_CONCAT(&mtd->saved, &mtd->children, entry);
TAILQ_INIT(&mtd->children);
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, mtd->filter);
mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, mtd->filter);
mtd->no_matches = TAILQ_EMPTY(&mtd->children);
if (mtd->no_matches)
mtd->buildcb(mtd->modedata, mtd->sort_type, &tag, NULL);
mtd->buildcb(mtd->modedata, &mtd->sort_crit, &tag, NULL);
mode_tree_free_items(&mtd->saved);
TAILQ_INIT(&mtd->saved);
@ -634,8 +634,9 @@ mode_tree_draw(struct mode_tree_data *mtd)
screen_write_cursormove(&ctx, 0, h, 0);
screen_write_box(&ctx, w, sy - h);
xasprintf(&text, " %s (sort: %s)", mti->name,
mtd->sort_list[mtd->sort_type]);
xasprintf(&text, " %s (sort: %s%s)", mti->name,
mtd->sort_list[mtd->sort_crit.field],
mtd->sort_crit.reversed ? ", reversed" : "");
if (w - 2 >= strlen(text)) {
screen_write_cursormove(&ctx, 1, h, 0);
screen_write_puts(&ctx, &gc0, "%s", text);
@ -993,9 +994,13 @@ mode_tree_key(struct mode_tree_data *mtd, struct client *c, key_code *key,
}
break;
case 'O':
mtd->sort_type++;
if (mtd->sort_type == mtd->sort_size)
mtd->sort_type = 0;
mtd->sort_crit.field++;
if (mtd->sort_crit.field == mtd->sort_size)
mtd->sort_crit.field = 0;
mode_tree_build(mtd);
break;
case 'r':
mtd->sort_crit.reversed = !mtd->sort_crit.reversed;
mode_tree_build(mtd);
break;
case KEYC_LEFT:

27
tmux.1
View File

@ -1692,7 +1692,7 @@ the end of the visible pane.
The default is to capture only the visible contents of the pane.
.It Xo
.Ic choose-client
.Op Fl NZ
.Op Fl NrZ
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl O Ar sort-order
@ -1721,7 +1721,8 @@ 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 order"
.It Li "O" Ta "Change sort field"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode"
.El
@ -1736,12 +1737,14 @@ If
is not given, "detach-client -t '%%'" is used.
.Pp
.Fl O
specifies the initial sort order: one of
specifies the initial sort field: one of
.Ql name ,
.Ql size ,
.Ql creation ,
or
.Ql activity .
.Fl r
reverses the sort order.
.Fl f
specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown.
@ -1753,7 +1756,7 @@ starts without the preview.
This command works only if at least one client is attached.
.It Xo
.Ic choose-tree
.Op Fl GNswZ
.Op Fl GNrswZ
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl O Ar sort-order
@ -1785,7 +1788,8 @@ The following keys may be used in tree mode:
.It Li "C-t" Ta "Tag all items"
.It Li "\&:" Ta "Run a command for each tagged item"
.It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort order"
.It Li "O" Ta "Change sort field"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode"
.El
@ -1800,11 +1804,13 @@ If
is not given, "switch-client -t '%%'" is used.
.Pp
.Fl O
specifies the initial sort order: one of
specifies the initial sort field: one of
.Ql index ,
.Ql name ,
or
.Ql time .
.Fl r
reverses the sort order.
.Fl f
specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown.
@ -4860,7 +4866,7 @@ The buffer commands are as follows:
.Bl -tag -width Ds
.It Xo
.Ic choose-buffer
.Op Fl NZ
.Op Fl NZr
.Op Fl F Ar format
.Op Fl f Ar filter
.Op Fl O Ar sort-order
@ -4887,7 +4893,8 @@ The following keys may be used in buffer mode:
.It Li "d" Ta "Delete selected buffer"
.It Li "D" Ta "Delete tagged buffers"
.It Li "f" Ta "Enter a format to filter items"
.It Li "O" Ta "Change sort order"
.It Li "O" Ta "Change sort field"
.It Li "r" Ta "Reverse sort order"
.It Li "v" Ta "Toggle preview"
.It Li "q" Ta "Exit mode"
.El
@ -4902,11 +4909,13 @@ If
is not given, "paste-buffer -b '%%'" is used.
.Pp
.Fl O
specifies the initial sort order: one of
specifies the initial sort field: one of
.Ql time ,
.Ql name
or
.Ql size .
.Fl r
reverses the sort order.
.Fl f
specifies an initial filter: the filter is a format - if it evaluates to zero,
the item in the list is not shown, otherwise it is shown.

9
tmux.h
View File

@ -1682,6 +1682,12 @@ struct spawn_context {
#define SPAWN_EMPTY 0x40
};
/* Mode tree sort order. */
struct mode_tree_sort_criteria {
u_int field;
int reversed;
};
/* tmux.c */
extern struct options *global_options;
extern struct options *global_s_options;
@ -2473,7 +2479,8 @@ u_int layout_set_next(struct window *);
u_int layout_set_previous(struct window *);
/* mode-tree.c */
typedef void (*mode_tree_build_cb)(void *, u_int, uint64_t *, const char *);
typedef void (*mode_tree_build_cb)(void *, struct mode_tree_sort_criteria *,
uint64_t *, const char *);
typedef void (*mode_tree_draw_cb)(void *, void *, struct screen_write_ctx *,
u_int, u_int);
typedef int (*mode_tree_search_cb)(void *, void *, const char *);

View File

@ -75,6 +75,7 @@ static const char *window_buffer_sort_list[] = {
"name",
"size"
};
static struct mode_tree_sort_criteria *window_buffer_sort;
struct window_buffer_itemdata {
const char *name;
@ -113,43 +114,29 @@ window_buffer_free_item(struct window_buffer_itemdata *item)
}
static int
window_buffer_cmp_name(const void *a0, const void *b0)
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;
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
int result = 0;
return (strcmp((*a)->name, (*b)->name));
}
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;
static int
window_buffer_cmp_time(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
/* Use WINDOW_BUFFER_BY_NAME as default order and tie breaker. */
if (result == 0)
result = strcmp((*a)->name, (*b)->name);
if ((*a)->order > (*b)->order)
return (-1);
if ((*a)->order < (*b)->order)
return (1);
return (strcmp((*a)->name, (*b)->name));
}
static int
window_buffer_cmp_size(const void *a0, const void *b0)
{
const struct window_buffer_itemdata *const *a = a0;
const struct window_buffer_itemdata *const *b = b0;
if ((*a)->size > (*b)->size)
return (-1);
if ((*a)->size < (*b)->size)
return (1);
return (strcmp((*a)->name, (*b)->name));
if (window_buffer_sort->reversed)
result = -result;
return (result);
}
static void
window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
const char *filter)
window_buffer_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
__unused uint64_t *tag, const char *filter)
{
struct window_buffer_modedata *data = modedata;
struct window_buffer_itemdata *item;
@ -175,20 +162,9 @@ window_buffer_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
item->order = paste_buffer_order(pb);
}
switch (sort_type) {
case WINDOW_BUFFER_BY_NAME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_name);
break;
case WINDOW_BUFFER_BY_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_time);
break;
case WINDOW_BUFFER_BY_SIZE:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_buffer_cmp_size);
break;
}
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;

View File

@ -75,6 +75,7 @@ static const char *window_client_sort_list[] = {
"creation",
"activity"
};
static struct mode_tree_sort_criteria *window_client_sort;
struct window_client_itemdata {
struct client *c;
@ -110,60 +111,48 @@ window_client_free_item(struct window_client_itemdata *item)
}
static int
window_client_cmp_name(const void *a0, const void *b0)
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 *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;
return (strcmp((*a)->c->name, (*b)->c->name));
}
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;
}
static int
window_client_cmp_size(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
/* Use WINDOW_CLIENT_BY_NAME as default order and tie breaker. */
if (result == 0)
result = strcmp(ca->name, cb->name);
if ((*a)->c->tty.sx < (*b)->c->tty.sx)
return (-1);
if ((*a)->c->tty.sx > (*b)->c->tty.sx)
return (1);
if ((*a)->c->tty.sy < (*b)->c->tty.sy)
return (-1);
if ((*a)->c->tty.sy > (*b)->c->tty.sy)
return (1);
return (strcmp((*a)->c->name, (*b)->c->name));
}
static int
window_client_cmp_creation_time(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, >))
return (-1);
if (timercmp(&(*a)->c->creation_time, &(*b)->c->creation_time, <))
return (1);
return (strcmp((*a)->c->name, (*b)->c->name));
}
static int
window_client_cmp_activity_time(const void *a0, const void *b0)
{
const struct window_client_itemdata *const *a = a0;
const struct window_client_itemdata *const *b = b0;
if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, >))
return (-1);
if (timercmp(&(*a)->c->activity_time, &(*b)->c->activity_time, <))
return (1);
return (strcmp((*a)->c->name, (*b)->c->name));
if (window_client_sort->reversed)
result = -result;
return (result);
}
static void
window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
const char *filter)
window_client_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
__unused uint64_t *tag, const char *filter)
{
struct window_client_modedata *data = modedata;
struct window_client_itemdata *item;
@ -187,24 +176,9 @@ window_client_build(void *modedata, u_int sort_type, __unused uint64_t *tag,
c->references++;
}
switch (sort_type) {
case WINDOW_CLIENT_BY_NAME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_name);
break;
case WINDOW_CLIENT_BY_SIZE:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_size);
break;
case WINDOW_CLIENT_BY_CREATION_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_creation_time);
break;
case WINDOW_CLIENT_BY_ACTIVITY_TIME:
qsort(data->item_list, data->item_size, sizeof *data->item_list,
window_client_cmp_activity_time);
break;
}
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];

View File

@ -89,6 +89,7 @@ static const char *window_tree_sort_list[] = {
"name",
"time"
};
static struct mode_tree_sort_criteria *window_tree_sort;
enum window_tree_type {
WINDOW_TREE_NONE,
@ -184,62 +185,92 @@ window_tree_free_item(struct window_tree_itemdata *item)
}
static int
window_tree_cmp_session_name(const void *a0, const void *b0)
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 *const *a = a0;
const struct session *const *b = b0;
const struct session *sa = *a;
const struct session *sb = *b;
int result;
return (strcmp((*a)->name, (*b)->name));
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_session_time(const void *a0, const void *b0)
window_tree_cmp_window(const void *a0, const void *b0)
{
const struct session *const *a = a0;
const struct session *const *b = 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;
if (timercmp(&(*a)->activity_time, &(*b)->activity_time, >))
return (-1);
if (timercmp(&(*a)->activity_time, &(*b)->activity_time, <))
return (1);
return (strcmp((*a)->name, (*b)->name));
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_window_name(const void *a0, const void *b0)
window_tree_cmp_pane(const void *a0, const void *b0)
{
const struct winlink *const *a = a0;
const struct winlink *const *b = b0;
const struct window_pane *const *a = a0;
const struct window_pane *const *b = b0;
int result;
return (strcmp((*a)->window->name, (*b)->window->name));
}
static int
window_tree_cmp_window_time(const void *a0, const void *b0)
{
const struct winlink *const *a = a0;
const struct winlink *const *b = b0;
if (timercmp(&(*a)->window->activity_time,
&(*b)->window->activity_time, >))
return (-1);
if (timercmp(&(*a)->window->activity_time,
&(*b)->window->activity_time, <))
return (1);
return (strcmp((*a)->window->name, (*b)->window->name));
}
static int
window_tree_cmp_pane_time(const void *a0, const void *b0)
{
const struct window_pane *const *a = a0;
const struct window_pane *const *b = b0;
if ((*a)->active_point < (*b)->active_point)
return (-1);
if ((*a)->active_point > (*b)->active_point)
return (1);
return (0);
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.
*/
result = (*a)->id - (*b)->id;
}
if (window_tree_sort->reversed)
result *= -1;
return (result);
}
static void
@ -285,8 +316,9 @@ window_tree_filter_pane(struct session *s, struct winlink *wl,
}
static int
window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
u_int sort_type, struct mode_tree_item *parent, const char *filter)
window_tree_build_window(struct session *s, struct winlink *wl,
void* modedata, struct mode_tree_sort_criteria *sort_crit,
struct mode_tree_item *parent, const char *filter)
{
struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item;
@ -335,16 +367,8 @@ window_tree_build_window(struct session *s, struct winlink *wl, void* modedata,
if (n == 0)
goto empty;
switch (sort_type) {
case WINDOW_TREE_BY_INDEX:
break;
case WINDOW_TREE_BY_NAME:
/* Panes don't have names, so leave in number order. */
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_pane_time);
break;
}
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);
@ -360,7 +384,7 @@ empty:
static void
window_tree_build_session(struct session *s, void* modedata,
u_int sort_type, const char *filter)
struct mode_tree_sort_criteria *sort_crit, const char *filter)
{
struct window_tree_modedata *data = modedata;
struct window_tree_itemdata *item;
@ -392,20 +416,12 @@ window_tree_build_session(struct session *s, void* modedata,
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = wl;
}
switch (sort_type) {
case WINDOW_TREE_BY_INDEX:
break;
case WINDOW_TREE_BY_NAME:
qsort(l, n, sizeof *l, window_tree_cmp_window_name);
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_window_time);
break;
}
window_tree_sort = sort_crit;
qsort(l, n, sizeof *l, window_tree_cmp_window);
empty = 0;
for (i = 0; i < n; i++) {
if (!window_tree_build_window(s, l[i], modedata, sort_type, mti,
if (!window_tree_build_window(s, l[i], modedata, sort_crit, mti,
filter))
empty++;
}
@ -418,8 +434,8 @@ window_tree_build_session(struct session *s, void* modedata,
}
static void
window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
const char *filter)
window_tree_build(void *modedata, struct mode_tree_sort_criteria *sort_crit,
uint64_t *tag, const char *filter)
{
struct window_tree_modedata *data = modedata;
struct session *s, **l;
@ -446,19 +462,11 @@ window_tree_build(void *modedata, u_int sort_type, uint64_t *tag,
l = xreallocarray(l, n + 1, sizeof *l);
l[n++] = s;
}
switch (sort_type) {
case WINDOW_TREE_BY_INDEX:
break;
case WINDOW_TREE_BY_NAME:
qsort(l, n, sizeof *l, window_tree_cmp_session_name);
break;
case WINDOW_TREE_BY_TIME:
qsort(l, n, sizeof *l, window_tree_cmp_session_time);
break;
}
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_type, filter);
window_tree_build_session(l[i], modedata, sort_crit, filter);
free(l);
switch (data->type) {