From 44dbf7ce91d35bde05a6fb239b0b3d626181afa3 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sat, 22 Jul 2023 12:45:51 +1000 Subject: [PATCH] Add doc:list-sort and sort command name for completion. doc:list-sort can sort entries in a doc-list based on an attribute of choice. emacs command listing uses this. Signed-off-by: NeilBrown --- DOC/TODO.md | 4 +- doc-list.c | 104 +++++++++++++++++++++++++++++++++++++++++++++++++++ mode-emacs.c | 1 + 3 files changed, 107 insertions(+), 2 deletions(-) diff --git a/DOC/TODO.md b/DOC/TODO.md index 967c25af..24f7dbe7 100644 --- a/DOC/TODO.md +++ b/DOC/TODO.md @@ -33,7 +33,7 @@ the file. - [X] menu for address completions in email-compose - [X] Change times_up() to use pane_too_long() -- [ ] change :A-x menu to use doc-list - add sorting +- [X] change :A-x menu to use doc-list - add sorting - [ ] Change render-lines to handle centring and right-align in flush_line - [ ] Teach render-lines to pad spaces to left/right align text @@ -275,7 +275,7 @@ Module features chars. - [ ] split some generic functionality like arrows and mouse clicks into a separate module to be shared with other edit modes. -- [ ] sort the command names for command-completion? +- [X] sort the command names for command-completion? Currently lines are inserted into buffer. I need to store in an array first, then qsort() - [ ] Do I want a 'truncated' marker at start/end of line when not diff --git a/doc-list.c b/doc-list.c index f3aa98e1..d1dc9504 100644 --- a/doc-list.c +++ b/doc-list.c @@ -144,6 +144,109 @@ DEF_CMD(list_add_elmnt) return 1; } +static char *key(struct list_head *le, const char *keyattr safe) +{ + struct elmnt *e; + char *ret; + if (le == NULL) + return NULL; + e = container_of(le, struct elmnt, list); + ret = attr_find(e->attrs, keyattr); + return ret ?: ""; +} + +static void sort_list(struct list_head *lst safe, const char *keyattr safe) +{ + struct list_head *de[2]; + struct list_head *l; + + if (list_empty(lst)) + return; + /* Convert to NULL terminated singly-linked list for sorting */ + lst->prev->next = safe_cast NULL; + + de[0] = lst->next; + de[1] = NULL; + + do { + struct list_head ** safe dep[2]; + struct list_head *d[2]; + int curr = 0; + char *prev = ""; + int next = 0; + + dep[0] = &de[0]; + dep[1] = &de[1]; + d[0] = de[0]; + d[1] = de[1]; + + /* d[0] and d[1] are two lists to be merged and split. + * The results will be put in de[0] and de[1]. + * dep[0] and dep[1] are end pointers to de[0] and de[1] so far. + * + * Take least of d[0] and d[1]. + * If it is larger than prev, add to + * dep[curr], else swap curr then add + */ + while (d[0] || d[1]) { + char *kn = key(d[next], keyattr); + char *kp = key(d[1-next], keyattr); + if (kn == NULL || + (kp != NULL && + !((strcmp(prev, kp) <= 0) + ^(strcmp(kp, kn) <= 0) + ^(strcmp(kn, prev) <= 0))) + ) { + char *t = kn; + kn = kp; + kp = t; + next = 1 - next; + } + + if (!d[next] || !kn) + break; + if (strcmp(kn, prev) < 0) + curr = 1 - curr; + prev = kn; + *dep[curr] = d[next]; + dep[curr] = &d[next]->next; + d[next] = d[next]->next; + } + *dep[0] = NULL; + *dep[1] = NULL; + } while (de[0] && de[1]); + + /* Now re-assemble the doublely-linked list */ + if (de[0]) + lst->next = de[0]; + else + lst->next = safe_cast de[1]; + l = lst; + + while ((void*)l->next) { + l->next->prev = l; + l = l->next; + } + l->next = lst; + lst->prev = l; +} + +DEF_CMD(list_sort) +{ + struct list *l = &ci->home->doc_data; + struct mark *m; + + if (!ci->str) + return Enoarg; + /* First move all marks to end */ + for (m = mark_first(&l->doc); m ; m = mark_next(m)) { + m->ref.p = NULL; + m->ref.i = 0; + } + sort_list(&l->content, ci->str); + return 1; +} + static struct map *list_map; DEF_LOOKUP_CMD(list_handle, list_map); @@ -185,6 +288,7 @@ static void list_init_map(void) key_add(list_map, "doc:get-attr", &list_get_attr); key_add(list_map, "doc:shares-ref", &list_shares_ref); key_add(list_map, "doc:list-add", &list_add_elmnt); + key_add(list_map, "doc:list-sort", &list_sort); key_add(list_map, "Close", &list_close); } diff --git a/mode-emacs.c b/mode-emacs.c index be2a4ed9..f9e20949 100644 --- a/mode-emacs.c +++ b/mode-emacs.c @@ -2102,6 +2102,7 @@ REDEF_CMD(emacs_cmd_complete) cr.p = doc; call_comm("keymap:list", ci->focus, &cr.c, 0, NULL, "interactive-cmd-"); + call("doc:list-sort", doc, 0, NULL, "cmd"); pop = call_ret(pane, "PopupTile", ci->focus, 0, NULL, "DM1r"); if (!pop) goto fail; -- 2.39.5