completion-sorting.diff
changeset 50 1c5d368913c9
child 51 5e5992999357
equal deleted inserted replaced
49:92fdcdb3ccd0 50:1c5d368913c9
       
     1 # HG changeset patch
       
     2 # Parent 3c7cebacae165329047f6859c26224e9c8a67807
       
     3 Make completion sorting order configurable
       
     4 
       
     5   * Use allocated plain array for categories
       
     6   * Use callbacks for dynamic completions (private for now)
       
     7   * Add compl_set_flags() to allow user to set completion order
       
     8   * [todo] Test ordering
       
     9   * [todo] Bump API (compatibly)
       
    10   * [todo] Reallocate array, when need more completions
       
    11   * [todo] Design and publish interface to dynamic completion cbs
       
    12 
       
    13 diff -r 3c7cebacae16 mcabber/mcabber/compl.c
       
    14 --- a/mcabber/mcabber/compl.c	Wed Oct 17 02:33:43 2012 +0300
       
    15 +++ b/mcabber/mcabber/compl.c	Wed Oct 17 03:00:30 2012 +0300
       
    16 @@ -2,7 +2,7 @@
       
    17   * compl.c      -- Completion system
       
    18   *
       
    19   * Copyright (C) 2005-2010 Mikael Berthe <mikael@lilotux.net>
       
    20 - * Copyright (C) 2009,2010 Myhailo Danylenko <isbear@ukrpost.net>
       
    21 + * Copyright (C) 2009-2012 Myhailo Danylenko <isbear@ukrpost.net>
       
    22   *
       
    23   * This program is free software; you can redistribute it and/or modify
       
    24   * it under the terms of the GNU General Public License as published by
       
    25 @@ -47,49 +47,96 @@
       
    26    GSList *next;         // pointer to next completion to try
       
    27  } compl;
       
    28  
       
    29 +typedef GSList *(*compl_handler_t) (void); // XXX userdata? *dynlist?
       
    30 +
       
    31  // Category structure
       
    32  typedef struct {
       
    33 -  guint64 flag;
       
    34 +  guint flags;
       
    35    GSList *words;
       
    36 +  compl_handler_t dynamic;
       
    37  } category;
       
    38  
       
    39 -static GSList *Categories;
       
    40 +#define COMPL_CAT_BUILTIN   0x01
       
    41 +#define COMPL_CAT_ACTIVE    0x02
       
    42 +#define COMPL_CAT_DYNAMIC   0x04
       
    43 +#define COMPL_CAT_REVERSE   0x10
       
    44 +#define COMPL_CAT_NOSORT    0x20
       
    45 +
       
    46 +#define COMPL_CAT_USERFLAGS 0x30
       
    47 +
       
    48  static compl *InputCompl;
       
    49 +static category *Categories;
       
    50 +static guint num_categories;
       
    51  
       
    52 -#ifdef MODULES_ENABLE
       
    53 -static guint64 registered_cats;
       
    54 +// Dynamic completions callbacks
       
    55 +static GSList *compl_dyn_group (void)
       
    56 +{
       
    57 +  return compl_list(ROSTER_TYPE_GROUP);
       
    58 +}
       
    59  
       
    60 -static inline void register_builtin_cat(guint c) {
       
    61 -  registered_cats |= 1UL << (c-1);
       
    62 +static GSList *compl_dyn_user (void)
       
    63 +{
       
    64 +  return compl_list(ROSTER_TYPE_USER);
       
    65 +}
       
    66 +
       
    67 +static GSList *compl_dyn_resource (void)
       
    68 +{
       
    69 +  return buddy_getresources_locale(NULL);
       
    70 +}
       
    71 +
       
    72 +static GSList *compl_dyn_events (void)
       
    73 +{
       
    74 +  GSList *compl = evs_geteventslist();
       
    75 +  GSList *cel;
       
    76 +  for (cel = compl; cel; cel = cel->next)
       
    77 +    cel->data = g_strdup(cel->data);
       
    78 +  compl = g_slist_append(compl, g_strdup("list"));
       
    79 +  return compl;
       
    80 +}
       
    81 +  
       
    82 +static inline void register_builtin_cat(guint c, compl_handler_t dynamic) {
       
    83 +  Categories[c-1].flags = COMPL_CAT_BUILTIN | COMPL_CAT_ACTIVE;
       
    84 +  if (dynamic != NULL) {
       
    85 +    Categories[c-1].flags |= COMPL_CAT_DYNAMIC;
       
    86 +    Categories[c-1].dynamic = dynamic;
       
    87 +  }
       
    88  }
       
    89  
       
    90  void compl_init_system(void)
       
    91  {
       
    92 +#ifdef MODULES_ENABLE
       
    93 +  num_categories = 64; // XXX
       
    94 +#else
       
    95 +  num_categories = COMPL_MODULE;
       
    96 +#endif
       
    97 +  Categories = g_new0(category, num_categories);
       
    98 +
       
    99    // Builtin completion categories:
       
   100 -  register_builtin_cat(COMPL_CMD);
       
   101 -  register_builtin_cat(COMPL_JID);
       
   102 -  register_builtin_cat(COMPL_URLJID);
       
   103 -  register_builtin_cat(COMPL_NAME);
       
   104 -  register_builtin_cat(COMPL_STATUS);
       
   105 -  register_builtin_cat(COMPL_FILENAME);
       
   106 -  register_builtin_cat(COMPL_ROSTER);
       
   107 -  register_builtin_cat(COMPL_BUFFER);
       
   108 -  register_builtin_cat(COMPL_GROUP);
       
   109 -  register_builtin_cat(COMPL_GROUPNAME);
       
   110 -  register_builtin_cat(COMPL_MULTILINE);
       
   111 -  register_builtin_cat(COMPL_ROOM);
       
   112 -  register_builtin_cat(COMPL_RESOURCE);
       
   113 -  register_builtin_cat(COMPL_AUTH);
       
   114 -  register_builtin_cat(COMPL_REQUEST);
       
   115 -  register_builtin_cat(COMPL_EVENTS);
       
   116 -  register_builtin_cat(COMPL_EVENTSID);
       
   117 -  register_builtin_cat(COMPL_PGP);
       
   118 -  register_builtin_cat(COMPL_COLOR);
       
   119 -  register_builtin_cat(COMPL_OTR);
       
   120 -  register_builtin_cat(COMPL_OTRPOLICY);
       
   121 -  register_builtin_cat(COMPL_MODULE);
       
   122 +  register_builtin_cat(COMPL_CMD, NULL);
       
   123 +  register_builtin_cat(COMPL_JID, compl_dyn_user);
       
   124 +  register_builtin_cat(COMPL_URLJID, NULL);
       
   125 +  register_builtin_cat(COMPL_NAME, NULL);
       
   126 +  register_builtin_cat(COMPL_STATUS, NULL);
       
   127 +  register_builtin_cat(COMPL_FILENAME, NULL);
       
   128 +  register_builtin_cat(COMPL_ROSTER, NULL);
       
   129 +  register_builtin_cat(COMPL_BUFFER, NULL);
       
   130 +  register_builtin_cat(COMPL_GROUP, NULL);
       
   131 +  register_builtin_cat(COMPL_GROUPNAME, compl_dyn_group);
       
   132 +  register_builtin_cat(COMPL_MULTILINE, NULL);
       
   133 +  register_builtin_cat(COMPL_ROOM, NULL);
       
   134 +  register_builtin_cat(COMPL_RESOURCE, compl_dyn_resource);
       
   135 +  register_builtin_cat(COMPL_AUTH, NULL);
       
   136 +  register_builtin_cat(COMPL_REQUEST, NULL);
       
   137 +  register_builtin_cat(COMPL_EVENTS, NULL);
       
   138 +  register_builtin_cat(COMPL_EVENTSID, compl_dyn_events);
       
   139 +  register_builtin_cat(COMPL_PGP, NULL);
       
   140 +  register_builtin_cat(COMPL_COLOR, NULL);
       
   141 +  register_builtin_cat(COMPL_OTR, NULL);
       
   142 +  register_builtin_cat(COMPL_OTRPOLICY, NULL);
       
   143 +  register_builtin_cat(COMPL_MODULE, NULL);
       
   144  }
       
   145  
       
   146 +#ifdef MODULES_ENABLE
       
   147  //  compl_new_category()
       
   148  // Reserves id for new completion category.
       
   149  // Returns 0, if no more categories can be allocated.
       
   150 @@ -97,32 +144,68 @@
       
   151  // as it is likely to change in future.
       
   152  guint compl_new_category(void)
       
   153  {
       
   154 -  const guint maxcat = 8 * sizeof (registered_cats);
       
   155    guint i = 0;
       
   156 -  while ((registered_cats >> i) & 1 && i < maxcat)
       
   157 -    i++;
       
   158 -  if (i >= maxcat)
       
   159 -    return 0;
       
   160 -  else {
       
   161 -    guint64 id = 1 << i;
       
   162 -    registered_cats |= id;
       
   163 -    return i+1;
       
   164 +  for (; i < num_categories; i++) {
       
   165 +    if (!(Categories[i].flags & COMPL_CAT_ACTIVE)) {
       
   166 +      Categories[i].flags = COMPL_CAT_ACTIVE;
       
   167 +      Categories[i].words = NULL;
       
   168 +      return i+1;
       
   169 +    }
       
   170    }
       
   171 +  // XXX realloc
       
   172 +  scr_log_print(LPRINT_LOGNORM, "Warning: run out of completion categories!");
       
   173 +  return 0;
       
   174  }
       
   175  
       
   176  //  compl_del_category(id)
       
   177  // Frees reserved id for category.
       
   178  // Note, that for now it not validates its input, so, be careful
       
   179  // and specify exactly what you get from compl_new_category.
       
   180 -void compl_del_category(guint id)
       
   181 +void compl_del_category(guint compl)
       
   182  {
       
   183 -  if (!id) {
       
   184 +  if (!compl) {
       
   185      scr_log_print(LPRINT_LOGNORM, "Error: compl_del_category() - "
       
   186 -                  "Invalid category.");
       
   187 +                                  "Invalid category.");
       
   188      return;
       
   189    }
       
   190 -  id--;
       
   191 -  registered_cats &= ~(1<<id);
       
   192 +  compl--;
       
   193 +  if ((compl >= num_categories) ||
       
   194 +      (Categories[compl].flags & COMPL_CAT_BUILTIN)) {
       
   195 +    scr_log_print(LPRINT_DEBUG, "Error: compl_del_category() "
       
   196 +                                "Invalid category.");
       
   197 +    return;
       
   198 +  }
       
   199 +
       
   200 +  Categories[compl].flags = 0;
       
   201 +  // XXX free words
       
   202 +}
       
   203 +
       
   204 +//  compl_set_sorting_order(category,order)
       
   205 +// Sets sorting order for given category.
       
   206 +// In future can be merged with new_category, set more flags,
       
   207 +// maybe even set dynamic callback.
       
   208 +void compl_set_flags(guint compl, guint new_flags)
       
   209 +{
       
   210 +  if (!compl) {
       
   211 +    scr_log_print(LPRINT_LOGNORM, "Error: compl_set_flags() - "
       
   212 +                                  "Invalid category.");
       
   213 +    return;
       
   214 +  }
       
   215 +  compl--;
       
   216 +  if (compl < num_categories) {
       
   217 +    guint flags = Categories[compl].flags;
       
   218 +    if (flags & COMPL_CAT_BUILTIN)
       
   219 +      scr_log_print(LPRINT_DEBUG, "Error: compl_set_flags() - "
       
   220 +                                  "Invalid category.");
       
   221 +    else if (!(flags & COMPL_CAT_ACTIVE))
       
   222 +      scr_log_print(LPRINT_DEBUG, "Error: compl_set_flags() - "
       
   223 +                                  "Invalid category.");
       
   224 +    else
       
   225 +      Categories[compl].flags = (flags & ~COMPL_CAT_USERFLAGS)
       
   226 +        | ((new_flags << 2) & COMPL_CAT_USERFLAGS);
       
   227 +  } else
       
   228 +    scr_log_print(LPRINT_DEBUG, "Error: compl_set_flags() - "
       
   229 +                                "Invalid category.");
       
   230  }
       
   231  #endif
       
   232  
       
   233 @@ -222,13 +305,30 @@
       
   234  
       
   235  /* Categories functions */
       
   236  
       
   237 +static gint compl_sort_forward(gconstpointer a, gconstpointer b)
       
   238 +{
       
   239 +  return g_ascii_strcasecmp((const gchar *)a, (const gchar *)b);
       
   240 +}
       
   241 +
       
   242 +static gint compl_sort_reverse(gconstpointer a, gconstpointer b)
       
   243 +{
       
   244 +  return -g_ascii_strcasecmp((const gchar *)a, (const gchar *)b);
       
   245 +}
       
   246 +
       
   247 +static gint compl_sort_append(gconstpointer a, gconstpointer b)
       
   248 +{
       
   249 +  return 1; // XXX
       
   250 +}
       
   251 +
       
   252 +static gint compl_sort_prepend(gconstpointer a, gconstpointer b)
       
   253 +{
       
   254 +  return -1; // XXX
       
   255 +}
       
   256 +
       
   257  //  compl_add_category_word(categ, command)
       
   258  // Adds a keyword as a possible completion in category categ.
       
   259  void compl_add_category_word(guint categ, const gchar *word)
       
   260  {
       
   261 -  guint64 catv;
       
   262 -  GSList *sl_cat;
       
   263 -  category *cat;
       
   264    char *nword;
       
   265  
       
   266    if (!categ) {
       
   267 @@ -236,21 +336,15 @@
       
   268                    "Invalid category.");
       
   269      return;
       
   270    }
       
   271 -
       
   272 +  
       
   273    categ--;
       
   274 -  catv = 1UL << categ;
       
   275 -
       
   276 -  // Look for category
       
   277 -  for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
       
   278 -    if (catv == ((category*)sl_cat->data)->flag)
       
   279 -      break;
       
   280 +  
       
   281 +  if ((categ > num_categories) ||
       
   282 +      !(Categories[categ].flags & COMPL_CAT_ACTIVE)) {
       
   283 +    scr_log_print(LPRINT_LOGNORM, "Error: compl_add_category_word() - "
       
   284 +                  "Invalid category.");
       
   285 +    return;
       
   286    }
       
   287 -  if (!sl_cat) {   // Category not found, let's create it
       
   288 -    cat = g_new0(category, 1);
       
   289 -    cat->flag = catv;
       
   290 -    Categories = g_slist_append(Categories, cat);
       
   291 -  } else
       
   292 -    cat = (category*)sl_cat->data;
       
   293  
       
   294    // If word is not space-terminated, we add one trailing space
       
   295    for (nword = (char*)word; *nword; nword++)
       
   296 @@ -262,20 +356,28 @@
       
   297      nword = g_strdup(word);
       
   298    }
       
   299  
       
   300 -  if (g_slist_find_custom(cat->words, nword, (GCompareFunc)g_strcmp0) != NULL)
       
   301 -    return;
       
   302 +  if (g_slist_find_custom(Categories[categ].words, nword,
       
   303 +                          (GCompareFunc)g_strcmp0) == NULL) {
       
   304 +    guint flags = Categories[categ].flags;
       
   305 +    GCompareFunc comparator = compl_sort_forward;
       
   306 +    if (flags & COMPL_CAT_NOSORT) {
       
   307 +      if (flags & COMPL_CAT_REVERSE)
       
   308 +        comparator = compl_sort_prepend;
       
   309 +      else
       
   310 +        comparator = compl_sort_append;
       
   311 +    } else if (flags & COMPL_CAT_REVERSE)
       
   312 +      comparator = compl_sort_reverse;
       
   313  
       
   314 -  cat->words = g_slist_insert_sorted(cat->words, nword,
       
   315 -                                     (GCompareFunc)g_ascii_strcasecmp);
       
   316 +    Categories[categ].words = g_slist_insert_sorted
       
   317 +                                  (Categories[categ].words, nword, comparator);
       
   318 +  }
       
   319  }
       
   320  
       
   321  //  compl_del_category_word(categ, command)
       
   322  // Removes a keyword from category categ in completion list.
       
   323  void compl_del_category_word(guint categ, const gchar *word)
       
   324  {
       
   325 -  guint64 catv;
       
   326 -  GSList *sl_cat, *sl_elt;
       
   327 -  category *cat;
       
   328 +  GSList *sl_elt;
       
   329    char *nword;
       
   330  
       
   331    if (!categ) {
       
   332 @@ -285,16 +387,13 @@
       
   333    }
       
   334  
       
   335    categ--;
       
   336 -  catv = 1UL << categ;
       
   337  
       
   338 -  // Look for category
       
   339 -  for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
       
   340 -    if (catv == ((category*)sl_cat->data)->flag)
       
   341 -      break;
       
   342 +  if ((categ > num_categories) ||
       
   343 +      !(Categories[categ].flags & COMPL_CAT_ACTIVE)) {
       
   344 +    scr_log_print(LPRINT_LOGNORM, "Error: compl_del_category_word() - "
       
   345 +                  "Invalid category.");
       
   346 +    return;
       
   347    }
       
   348 -  if (!sl_cat) return;   // Category not found, finished!
       
   349 -
       
   350 -  cat = (category*)sl_cat->data;
       
   351  
       
   352    // If word is not space-terminated, we add one trailing space
       
   353    for (nword = (char*)word; *nword; nword++)
       
   354 @@ -306,11 +405,12 @@
       
   355      nword = g_strdup(word);
       
   356    }
       
   357  
       
   358 -  sl_elt = cat->words;
       
   359 +  sl_elt = Categories[categ].words;
       
   360    while (sl_elt) {
       
   361      if (!strcasecmp((char*)sl_elt->data, nword)) {
       
   362        g_free(sl_elt->data);
       
   363 -      cat->words = g_slist_delete_link(cat->words, sl_elt);
       
   364 +      Categories[categ].words = g_slist_delete_link
       
   365 +                                             (Categories[categ].words, sl_elt);
       
   366        break; // Only remove first occurence
       
   367      }
       
   368      sl_elt = g_slist_next(sl_elt);
       
   369 @@ -323,48 +423,28 @@
       
   370  // whole list after use.
       
   371  GSList *compl_get_category_list(guint categ, guint *dynlist)
       
   372  {
       
   373 -  guint64 cat_flags;
       
   374 -  GSList *sl_cat;
       
   375 -
       
   376    if (!categ) {
       
   377      scr_log_print(LPRINT_LOGNORM, "Error: compl_get_category_list() - "
       
   378                    "Invalid category.");
       
   379      return NULL;
       
   380    }
       
   381  
       
   382 -  *dynlist = FALSE;
       
   383 -  cat_flags = 1UL << (categ - 1);
       
   384 +  categ --;
       
   385  
       
   386 -  // Look for the category
       
   387 -  for (sl_cat=Categories; sl_cat; sl_cat = g_slist_next(sl_cat)) {
       
   388 -    if (cat_flags == ((category*)sl_cat->data)->flag)
       
   389 -      break;
       
   390 -  }
       
   391 -  if (sl_cat)       // Category was found, easy...
       
   392 -    return ((category*)sl_cat->data)->words;
       
   393 -
       
   394 -  // Handle dynamic SLists
       
   395 -  *dynlist = TRUE;
       
   396 -  if (categ == COMPL_GROUPNAME) {
       
   397 -    return compl_list(ROSTER_TYPE_GROUP);
       
   398 -  }
       
   399 -  if (categ == COMPL_JID) {
       
   400 -    return compl_list(ROSTER_TYPE_USER);
       
   401 -  }
       
   402 -  if (categ == COMPL_RESOURCE) {
       
   403 -    return buddy_getresources_locale(NULL);
       
   404 -  }
       
   405 -  if (categ == COMPL_EVENTSID) {
       
   406 -    GSList *compl = evs_geteventslist();
       
   407 -    GSList *cel;
       
   408 -    for (cel = compl; cel; cel = cel->next)
       
   409 -      cel->data = g_strdup(cel->data);
       
   410 -    compl = g_slist_append(compl, g_strdup("list"));
       
   411 -    return compl;
       
   412 +  if ((categ > num_categories) ||
       
   413 +      !(Categories[categ].flags & COMPL_CAT_ACTIVE)) {
       
   414 +    scr_log_print(LPRINT_LOGNORM, "Error: compl_get_category_list() - "
       
   415 +                  "Invalid category.");
       
   416 +    return NULL;
       
   417    }
       
   418  
       
   419 -  *dynlist = FALSE;
       
   420 -  return NULL;
       
   421 +  if (Categories[categ].flags & COMPL_CAT_DYNAMIC) {
       
   422 +    *dynlist = TRUE;
       
   423 +    return (*Categories[categ].dynamic) ();
       
   424 +  } else {
       
   425 +    *dynlist = FALSE;
       
   426 +    return Categories[categ].words;
       
   427 +  }
       
   428  }
       
   429  
       
   430  /* vim: set expandtab cindent cinoptions=>2\:2(0 sw=2 ts=2:  For Vim users... */
       
   431 diff -r 3c7cebacae16 mcabber/mcabber/compl.h
       
   432 --- a/mcabber/mcabber/compl.h	Wed Oct 17 02:33:43 2012 +0300
       
   433 +++ b/mcabber/mcabber/compl.h	Wed Oct 17 03:00:30 2012 +0300
       
   434 @@ -28,10 +28,17 @@
       
   435  #define COMPL_OTRPOLICY   21
       
   436  #define COMPL_MODULE      22
       
   437  
       
   438 +void compl_init_system(void); /* private */
       
   439 +
       
   440  #ifdef MODULES_ENABLE
       
   441 -void  compl_init_system(void);
       
   442 +#define COMPL_FLAGS_SORT     0x00
       
   443 +#define COMPL_FLAGS_REVERSE  0x10
       
   444 +#define COMPL_FLAGS_APPEND   0x20
       
   445 +#define COMPL_FLAGS_PREPEND  0x30
       
   446 +
       
   447  guint compl_new_category(void);
       
   448  void  compl_del_category(guint id);
       
   449 +void  compl_set_flags(guint id, guint flags);
       
   450  #endif
       
   451  
       
   452  void    compl_add_category_word(guint categ, const gchar *command);
       
   453 diff -r 3c7cebacae16 mcabber/mcabber/main.c
       
   454 --- a/mcabber/mcabber/main.c	Wed Oct 17 02:33:43 2012 +0300
       
   455 +++ b/mcabber/mcabber/main.c	Wed Oct 17 03:00:30 2012 +0300
       
   456 @@ -368,13 +368,13 @@
       
   457    }
       
   458  
       
   459    /* Initialize command system, roster and default key bindings */
       
   460 +  compl_init_system();
       
   461    cmd_init();
       
   462    roster_init();
       
   463    settings_init();
       
   464    scr_init_bindings();
       
   465    caps_init();
       
   466  #ifdef MODULES_ENABLE
       
   467 -  compl_init_system();
       
   468    modules_init();
       
   469  #endif
       
   470    /* Initialize charset */