Add completion-sorting.diff
authorMyhailo Danylenko <isbear@ukrpost.net>
Wed, 17 Oct 2012 03:07:22 +0300
changeset 50 1c5d368913c9
parent 49 92fdcdb3ccd0
child 51 5e5992999357
Add completion-sorting.diff
completion-sorting.diff
series
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/completion-sorting.diff	Wed Oct 17 03:07:22 2012 +0300
@@ -0,0 +1,470 @@
+# HG changeset patch
+# Parent 3c7cebacae165329047f6859c26224e9c8a67807
+Make completion sorting order configurable
+
+  * Use allocated plain array for categories
+  * Use callbacks for dynamic completions (private for now)
+  * Add compl_set_flags() to allow user to set completion order
+  * [todo] Test ordering
+  * [todo] Bump API (compatibly)
+  * [todo] Reallocate array, when need more completions
+  * [todo] Design and publish interface to dynamic completion cbs
+
+diff -r 3c7cebacae16 mcabber/mcabber/compl.c
+--- a/mcabber/mcabber/compl.c	Wed Oct 17 02:33:43 2012 +0300
++++ b/mcabber/mcabber/compl.c	Wed Oct 17 03:00:30 2012 +0300
+@@ -2,7 +2,7 @@
+  * compl.c      -- Completion system
+  *
+  * Copyright (C) 2005-2010 Mikael Berthe <mikael@lilotux.net>
+- * Copyright (C) 2009,2010 Myhailo Danylenko <isbear@ukrpost.net>
++ * Copyright (C) 2009-2012 Myhailo Danylenko <isbear@ukrpost.net>
+  *
+  * This program is free software; you can redistribute it and/or modify
+  * it under the terms of the GNU General Public License as published by
+@@ -47,49 +47,96 @@
+   GSList *next;         // pointer to next completion to try
+ } compl;
+ 
++typedef GSList *(*compl_handler_t) (void); // XXX userdata? *dynlist?
++
+ // Category structure
+ typedef struct {
+-  guint64 flag;
++  guint flags;
+   GSList *words;
++  compl_handler_t dynamic;
+ } category;
+ 
+-static GSList *Categories;
++#define COMPL_CAT_BUILTIN   0x01
++#define COMPL_CAT_ACTIVE    0x02
++#define COMPL_CAT_DYNAMIC   0x04
++#define COMPL_CAT_REVERSE   0x10
++#define COMPL_CAT_NOSORT    0x20
++
++#define COMPL_CAT_USERFLAGS 0x30
++
+ static compl *InputCompl;
++static category *Categories;
++static guint num_categories;
+ 
+-#ifdef MODULES_ENABLE
+-static guint64 registered_cats;
++// Dynamic completions callbacks
++static GSList *compl_dyn_group (void)
++{
++  return compl_list(ROSTER_TYPE_GROUP);
++}
+ 
+-static inline void register_builtin_cat(guint c) {
+-  registered_cats |= 1UL << (c-1);
++static GSList *compl_dyn_user (void)
++{
++  return compl_list(ROSTER_TYPE_USER);
++}
++
++static GSList *compl_dyn_resource (void)
++{
++  return buddy_getresources_locale(NULL);
++}
++
++static GSList *compl_dyn_events (void)
++{
++  GSList *compl = evs_geteventslist();
++  GSList *cel;
++  for (cel = compl; cel; cel = cel->next)
++    cel->data = g_strdup(cel->data);
++  compl = g_slist_append(compl, g_strdup("list"));
++  return compl;
++}
++  
++static inline void register_builtin_cat(guint c, compl_handler_t dynamic) {
++  Categories[c-1].flags = COMPL_CAT_BUILTIN | COMPL_CAT_ACTIVE;
++  if (dynamic != NULL) {
++    Categories[c-1].flags |= COMPL_CAT_DYNAMIC;
++    Categories[c-1].dynamic = dynamic;
++  }
+ }
+ 
+ void compl_init_system(void)
+ {
++#ifdef MODULES_ENABLE
++  num_categories = 64; // XXX
++#else
++  num_categories = COMPL_MODULE;
++#endif
++  Categories = g_new0(category, num_categories);
++
+   // Builtin completion categories:
+-  register_builtin_cat(COMPL_CMD);
+-  register_builtin_cat(COMPL_JID);
+-  register_builtin_cat(COMPL_URLJID);
+-  register_builtin_cat(COMPL_NAME);
+-  register_builtin_cat(COMPL_STATUS);
+-  register_builtin_cat(COMPL_FILENAME);
+-  register_builtin_cat(COMPL_ROSTER);
+-  register_builtin_cat(COMPL_BUFFER);
+-  register_builtin_cat(COMPL_GROUP);
+-  register_builtin_cat(COMPL_GROUPNAME);
+-  register_builtin_cat(COMPL_MULTILINE);
+-  register_builtin_cat(COMPL_ROOM);
+-  register_builtin_cat(COMPL_RESOURCE);
+-  register_builtin_cat(COMPL_AUTH);
+-  register_builtin_cat(COMPL_REQUEST);
+-  register_builtin_cat(COMPL_EVENTS);
+-  register_builtin_cat(COMPL_EVENTSID);
+-  register_builtin_cat(COMPL_PGP);
+-  register_builtin_cat(COMPL_COLOR);
+-  register_builtin_cat(COMPL_OTR);
+-  register_builtin_cat(COMPL_OTRPOLICY);
+-  register_builtin_cat(COMPL_MODULE);
++  register_builtin_cat(COMPL_CMD, NULL);
++  register_builtin_cat(COMPL_JID, compl_dyn_user);
++  register_builtin_cat(COMPL_URLJID, NULL);
++  register_builtin_cat(COMPL_NAME, NULL);
++  register_builtin_cat(COMPL_STATUS, NULL);
++  register_builtin_cat(COMPL_FILENAME, NULL);
++  register_builtin_cat(COMPL_ROSTER, NULL);
++  register_builtin_cat(COMPL_BUFFER, NULL);
++  register_builtin_cat(COMPL_GROUP, NULL);
++  register_builtin_cat(COMPL_GROUPNAME, compl_dyn_group);
++  register_builtin_cat(COMPL_MULTILINE, NULL);
++  register_builtin_cat(COMPL_ROOM, NULL);
++  register_builtin_cat(COMPL_RESOURCE, compl_dyn_resource);
++  register_builtin_cat(COMPL_AUTH, NULL);
++  register_builtin_cat(COMPL_REQUEST, NULL);
++  register_builtin_cat(COMPL_EVENTS, NULL);
++  register_builtin_cat(COMPL_EVENTSID, compl_dyn_events);
++  register_builtin_cat(COMPL_PGP, NULL);
++  register_builtin_cat(COMPL_COLOR, NULL);
++  register_builtin_cat(COMPL_OTR, NULL);
++  register_builtin_cat(COMPL_OTRPOLICY, NULL);
++  register_builtin_cat(COMPL_MODULE, NULL);
+ }
+ 
++#ifdef MODULES_ENABLE
+ //  compl_new_category()
+ // Reserves id for new completion category.
+ // Returns 0, if no more categories can be allocated.
+@@ -97,32 +144,68 @@
+ // as it is likely to change in future.
+ guint compl_new_category(void)
+ {
+-  const guint maxcat = 8 * sizeof (registered_cats);
+   guint i = 0;
+-  while ((registered_cats >> i) & 1 && i < maxcat)
+-    i++;
+-  if (i >= maxcat)
+-    return 0;
+-  else {
+-    guint64 id = 1 << i;
+-    registered_cats |= id;
+-    return i+1;
++  for (; i < num_categories; i++) {
++    if (!(Categories[i].flags & COMPL_CAT_ACTIVE)) {
++      Categories[i].flags = COMPL_CAT_ACTIVE;
++      Categories[i].words = NULL;
++      return i+1;
++    }
+   }
++  // XXX realloc
++  scr_log_print(LPRINT_LOGNORM, "Warning: run out of completion categories!");
++  return 0;
+ }
+ 
+ //  compl_del_category(id)
+ // Frees reserved id for category.
+ // Note, that for now it not validates its input, so, be careful
+ // and specify exactly what you get from compl_new_category.
+-void compl_del_category(guint id)
++void compl_del_category(guint compl)
+ {
+-  if (!id) {
++  if (!compl) {
+     scr_log_print(LPRINT_LOGNORM, "Error: compl_del_category() - "
+-                  "Invalid category.");
++                                  "Invalid category.");
+     return;
+   }
+-  id--;
+-  registered_cats &= ~(1<<id);
++  compl--;
++  if ((compl >= num_categories) ||
++      (Categories[compl].flags & COMPL_CAT_BUILTIN)) {
++    scr_log_print(LPRINT_DEBUG, "Error: compl_del_category() "
++                                "Invalid category.");
++    return;
++  }
++
++  Categories[compl].flags = 0;
++  // XXX free words
++}
++
++//  compl_set_sorting_order(category,order)
++// Sets sorting order for given category.
++// In future can be merged with new_category, set more flags,
++// maybe even set dynamic callback.
++void compl_set_flags(guint compl, guint new_flags)
++{
++  if (!compl) {
++    scr_log_print(LPRINT_LOGNORM, "Error: compl_set_flags() - "
++                                  "Invalid category.");
++    return;
++  }
++  compl--;
++  if (compl < num_categories) {
++    guint flags = Categories[compl].flags;
++    if (flags & COMPL_CAT_BUILTIN)
++      scr_log_print(LPRINT_DEBUG, "Error: compl_set_flags() - "
++                                  "Invalid category.");
++    else if (!(flags & COMPL_CAT_ACTIVE))
++      scr_log_print(LPRINT_DEBUG, "Error: compl_set_flags() - "
++                                  "Invalid category.");
++    else
++      Categories[compl].flags = (flags & ~COMPL_CAT_USERFLAGS)
++        | ((new_flags << 2) & COMPL_CAT_USERFLAGS);
++  } else
++    scr_log_print(LPRINT_DEBUG, "Error: compl_set_flags() - "
++                                "Invalid category.");
+ }
+ #endif
+ 
+@@ -222,13 +305,30 @@
+ 
+ /* Categories functions */
+ 
++static gint compl_sort_forward(gconstpointer a, gconstpointer b)
++{
++  return g_ascii_strcasecmp((const gchar *)a, (const gchar *)b);
++}
++
++static gint compl_sort_reverse(gconstpointer a, gconstpointer b)
++{
++  return -g_ascii_strcasecmp((const gchar *)a, (const gchar *)b);
++}
++
++static gint compl_sort_append(gconstpointer a, gconstpointer b)
++{
++  return 1; // XXX
++}
++
++static gint compl_sort_prepend(gconstpointer a, gconstpointer b)
++{
++  return -1; // XXX
++}
++
+ //  compl_add_category_word(categ, command)
+ // Adds a keyword as a possible completion in category categ.
+ void compl_add_category_word(guint categ, const gchar *word)
+ {
+-  guint64 catv;
+-  GSList *sl_cat;
+-  category *cat;
+   char *nword;
+ 
+   if (!categ) {
+@@ -236,21 +336,15 @@
+                   "Invalid category.");
+     return;
+   }
+-
++  
+   categ--;
+-  catv = 1UL << categ;
+-
+-  // Look for category
+-  for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
+-    if (catv == ((category*)sl_cat->data)->flag)
+-      break;
++  
++  if ((categ > num_categories) ||
++      !(Categories[categ].flags & COMPL_CAT_ACTIVE)) {
++    scr_log_print(LPRINT_LOGNORM, "Error: compl_add_category_word() - "
++                  "Invalid category.");
++    return;
+   }
+-  if (!sl_cat) {   // Category not found, let's create it
+-    cat = g_new0(category, 1);
+-    cat->flag = catv;
+-    Categories = g_slist_append(Categories, cat);
+-  } else
+-    cat = (category*)sl_cat->data;
+ 
+   // If word is not space-terminated, we add one trailing space
+   for (nword = (char*)word; *nword; nword++)
+@@ -262,20 +356,28 @@
+     nword = g_strdup(word);
+   }
+ 
+-  if (g_slist_find_custom(cat->words, nword, (GCompareFunc)g_strcmp0) != NULL)
+-    return;
++  if (g_slist_find_custom(Categories[categ].words, nword,
++                          (GCompareFunc)g_strcmp0) == NULL) {
++    guint flags = Categories[categ].flags;
++    GCompareFunc comparator = compl_sort_forward;
++    if (flags & COMPL_CAT_NOSORT) {
++      if (flags & COMPL_CAT_REVERSE)
++        comparator = compl_sort_prepend;
++      else
++        comparator = compl_sort_append;
++    } else if (flags & COMPL_CAT_REVERSE)
++      comparator = compl_sort_reverse;
+ 
+-  cat->words = g_slist_insert_sorted(cat->words, nword,
+-                                     (GCompareFunc)g_ascii_strcasecmp);
++    Categories[categ].words = g_slist_insert_sorted
++                                  (Categories[categ].words, nword, comparator);
++  }
+ }
+ 
+ //  compl_del_category_word(categ, command)
+ // Removes a keyword from category categ in completion list.
+ void compl_del_category_word(guint categ, const gchar *word)
+ {
+-  guint64 catv;
+-  GSList *sl_cat, *sl_elt;
+-  category *cat;
++  GSList *sl_elt;
+   char *nword;
+ 
+   if (!categ) {
+@@ -285,16 +387,13 @@
+   }
+ 
+   categ--;
+-  catv = 1UL << categ;
+ 
+-  // Look for category
+-  for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
+-    if (catv == ((category*)sl_cat->data)->flag)
+-      break;
++  if ((categ > num_categories) ||
++      !(Categories[categ].flags & COMPL_CAT_ACTIVE)) {
++    scr_log_print(LPRINT_LOGNORM, "Error: compl_del_category_word() - "
++                  "Invalid category.");
++    return;
+   }
+-  if (!sl_cat) return;   // Category not found, finished!
+-
+-  cat = (category*)sl_cat->data;
+ 
+   // If word is not space-terminated, we add one trailing space
+   for (nword = (char*)word; *nword; nword++)
+@@ -306,11 +405,12 @@
+     nword = g_strdup(word);
+   }
+ 
+-  sl_elt = cat->words;
++  sl_elt = Categories[categ].words;
+   while (sl_elt) {
+     if (!strcasecmp((char*)sl_elt->data, nword)) {
+       g_free(sl_elt->data);
+-      cat->words = g_slist_delete_link(cat->words, sl_elt);
++      Categories[categ].words = g_slist_delete_link
++                                             (Categories[categ].words, sl_elt);
+       break; // Only remove first occurence
+     }
+     sl_elt = g_slist_next(sl_elt);
+@@ -323,48 +423,28 @@
+ // whole list after use.
+ GSList *compl_get_category_list(guint categ, guint *dynlist)
+ {
+-  guint64 cat_flags;
+-  GSList *sl_cat;
+-
+   if (!categ) {
+     scr_log_print(LPRINT_LOGNORM, "Error: compl_get_category_list() - "
+                   "Invalid category.");
+     return NULL;
+   }
+ 
+-  *dynlist = FALSE;
+-  cat_flags = 1UL << (categ - 1);
++  categ --;
+ 
+-  // Look for the category
+-  for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
+-    if (cat_flags == ((category*)sl_cat->data)->flag)
+-      break;
+-  }
+-  if (sl_cat)       // Category was found, easy...
+-    return ((category*)sl_cat->data)->words;
+-
+-  // Handle dynamic SLists
+-  *dynlist = TRUE;
+-  if (categ == COMPL_GROUPNAME) {
+-    return compl_list(ROSTER_TYPE_GROUP);
+-  }
+-  if (categ == COMPL_JID) {
+-    return compl_list(ROSTER_TYPE_USER);
+-  }
+-  if (categ == COMPL_RESOURCE) {
+-    return buddy_getresources_locale(NULL);
+-  }
+-  if (categ == COMPL_EVENTSID) {
+-    GSList *compl = evs_geteventslist();
+-    GSList *cel;
+-    for (cel = compl; cel; cel = cel->next)
+-      cel->data = g_strdup(cel->data);
+-    compl = g_slist_append(compl, g_strdup("list"));
+-    return compl;
++  if ((categ > num_categories) ||
++      !(Categories[categ].flags & COMPL_CAT_ACTIVE)) {
++    scr_log_print(LPRINT_LOGNORM, "Error: compl_get_category_list() - "
++                  "Invalid category.");
++    return NULL;
+   }
+ 
+-  *dynlist = FALSE;
+-  return NULL;
++  if (Categories[categ].flags & COMPL_CAT_DYNAMIC) {
++    *dynlist = TRUE;
++    return (*Categories[categ].dynamic) ();
++  } else {
++    *dynlist = FALSE;
++    return Categories[categ].words;
++  }
+ }
+ 
+ /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
+diff -r 3c7cebacae16 mcabber/mcabber/compl.h
+--- a/mcabber/mcabber/compl.h	Wed Oct 17 02:33:43 2012 +0300
++++ b/mcabber/mcabber/compl.h	Wed Oct 17 03:00:30 2012 +0300
+@@ -28,10 +28,17 @@
+ #define COMPL_OTRPOLICY   21
+ #define COMPL_MODULE      22
+ 
++void compl_init_system(void); /* private */
++
+ #ifdef MODULES_ENABLE
+-void  compl_init_system(void);
++#define COMPL_FLAGS_SORT     0x00
++#define COMPL_FLAGS_REVERSE  0x10
++#define COMPL_FLAGS_APPEND   0x20
++#define COMPL_FLAGS_PREPEND  0x30
++
+ guint compl_new_category(void);
+ void  compl_del_category(guint id);
++void  compl_set_flags(guint id, guint flags);
+ #endif
+ 
+ void    compl_add_category_word(guint categ, const gchar *command);
+diff -r 3c7cebacae16 mcabber/mcabber/main.c
+--- a/mcabber/mcabber/main.c	Wed Oct 17 02:33:43 2012 +0300
++++ b/mcabber/mcabber/main.c	Wed Oct 17 03:00:30 2012 +0300
+@@ -368,13 +368,13 @@
+   }
+ 
+   /* Initialize command system, roster and default key bindings */
++  compl_init_system();
+   cmd_init();
+   roster_init();
+   settings_init();
+   scr_init_bindings();
+   caps_init();
+ #ifdef MODULES_ENABLE
+-  compl_init_system();
+   modules_init();
+ #endif
+   /* Initialize charset */
--- a/series	Sun Oct 14 19:11:07 2012 +0300
+++ b/series	Wed Oct 17 03:07:22 2012 +0300
@@ -1,3 +1,4 @@
+completion-sorting.diff
 switch-to-experimental.diff
 separate-extcmd.diff
 modularize-extcmd.diff