|
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 */ |