]> git.neil.brown.name Git - edlib.git/commitdiff
Add doc:list-sort and sort command name for completion.
authorNeilBrown <neil@brown.name>
Sat, 22 Jul 2023 02:45:51 +0000 (12:45 +1000)
committerNeilBrown <neil@brown.name>
Sat, 22 Jul 2023 02:45:51 +0000 (12:45 +1000)
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 <neil@brown.name>
DOC/TODO.md
doc-list.c
mode-emacs.c

index 967c25afbbd5516b4e58b8223efabaf5f60de310..24f7dbe7c3fe28265a7968beb3de854ea7258eff 100644 (file)
@@ -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
index f3aa98e137d5365f080e7fe9a71ccfba60ecab4c..d1dc9504d6a0450e4f1504435dfa18082e6ad22b 100644 (file)
@@ -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);
 }
 
index be2a4ed978dd090dc9f66d4b1a966ea77e006017..f9e20949b599315a94eff0269262338e3a4e9c4b 100644 (file)
@@ -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;