summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSébastien Helleu <flashcode@flashtux.org>2014-10-05 08:35:17 +0200
committerSébastien Helleu <flashcode@flashtux.org>2014-10-05 08:35:17 +0200
commitf53baf628ec99fa511053a8ad171aa19b61f9cd2 (patch)
treead67400bfa27698fbd53cd5dc3486419705afe53
parentc6eb5e6b12c120c720ea8129a3d80e7e349dde7f (diff)
parent7f4d9de2552a78e1d8f4f500c529b3aa6277b1ef (diff)
downloadweechat-f53baf628ec99fa511053a8ad171aa19b61f9cd2.zip
Merge branch 'arraylist'
-rw-r--r--po/POTFILES.in2
-rw-r--r--po/srcfiles.cmake2
-rw-r--r--src/core/CMakeLists.txt1
-rw-r--r--src/core/Makefile.am2
-rw-r--r--src/core/wee-arraylist.c632
-rw-r--r--src/core/wee-arraylist.h64
-rw-r--r--src/core/wee-completion.c5
-rw-r--r--src/gui/gui-bar-item.c27
-rw-r--r--src/gui/gui-completion.c419
-rw-r--r--src/gui/gui-completion.h13
-rw-r--r--src/plugins/plugin-api.c2
-rw-r--r--tests/CMakeLists.txt3
-rw-r--r--tests/Makefile.am7
-rw-r--r--tests/tests.cpp1
-rw-r--r--tests/unit/core/test-arraylist.cpp418
15 files changed, 1388 insertions, 210 deletions
diff --git a/po/POTFILES.in b/po/POTFILES.in
index e89ebdad6..c9681d5f4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,4 +1,6 @@
./doc/docgen.py
+./src/core/wee-arraylist.c
+./src/core/wee-arraylist.h
./src/core/wee-backtrace.c
./src/core/wee-backtrace.h
./src/core/weechat.c
diff --git a/po/srcfiles.cmake b/po/srcfiles.cmake
index 810fd3a31..a7d10d667 100644
--- a/po/srcfiles.cmake
+++ b/po/srcfiles.cmake
@@ -1,5 +1,7 @@
SET(WEECHAT_SOURCES
./doc/docgen.py
+./src/core/wee-arraylist.c
+./src/core/wee-arraylist.h
./src/core/wee-backtrace.c
./src/core/wee-backtrace.h
./src/core/weechat.c
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index ff2bc13b5..1ae0eaf37 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -21,6 +21,7 @@
set(LIB_CORE_SRC
weechat.c weechat.h
+wee-arraylist.c wee-arraylist.h
wee-backtrace.c wee-backtrace.h
wee-command.c wee-command.h
wee-completion.c wee-completion.h
diff --git a/src/core/Makefile.am b/src/core/Makefile.am
index 65b199263..c7276d8fe 100644
--- a/src/core/Makefile.am
+++ b/src/core/Makefile.am
@@ -23,6 +23,8 @@ noinst_LIBRARIES = lib_weechat_core.a
lib_weechat_core_a_SOURCES = weechat.c \
weechat.h \
+ wee-arraylist.c \
+ wee-arraylist.h \
wee-backtrace.c \
wee-backtrace.h \
wee-command.c \
diff --git a/src/core/wee-arraylist.c b/src/core/wee-arraylist.c
new file mode 100644
index 000000000..fbbbbef7d
--- /dev/null
+++ b/src/core/wee-arraylist.c
@@ -0,0 +1,632 @@
+/*
+ * wee-arraylist.c - array lists management
+ *
+ * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * WeeChat is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "weechat.h"
+#include "wee-arraylist.h"
+#include "wee-log.h"
+#include "wee-string.h"
+
+
+/*
+ * Creates a new arraylist.
+ *
+ * Returns pointer to arraylist, NULL if error.
+ */
+
+struct t_arraylist *
+arraylist_new (int initial_size,
+ int sorted,
+ int allow_duplicates,
+ t_arraylist_cmp *callback_cmp, void *callback_cmp_data,
+ t_arraylist_free *callback_free, void *callback_free_data)
+{
+ struct t_arraylist *new_arraylist;
+
+ /* check arguments */
+ if ((initial_size < 0) || !callback_cmp)
+ return NULL;
+
+ new_arraylist = malloc (sizeof (*new_arraylist));
+ if (!new_arraylist)
+ return NULL;
+
+ new_arraylist->size = 0;
+ if (initial_size > 0)
+ {
+ new_arraylist->size_alloc = initial_size;
+ new_arraylist->size_alloc_min = initial_size;
+ new_arraylist->data = calloc (initial_size,
+ sizeof (*new_arraylist->data));
+ if (!new_arraylist->data)
+ {
+ free (new_arraylist);
+ return NULL;
+ }
+ }
+ else
+ {
+ new_arraylist->size_alloc = 0;
+ new_arraylist->size_alloc_min = 0;
+ new_arraylist->data = NULL;
+ }
+ new_arraylist->sorted = sorted;
+ new_arraylist->allow_duplicates = allow_duplicates;
+ new_arraylist->callback_cmp = callback_cmp;
+ new_arraylist->callback_cmp_data = callback_cmp_data;
+ new_arraylist->callback_free = callback_free;
+ new_arraylist->callback_free_data = callback_free_data;
+
+ return new_arraylist;
+}
+
+/*
+ * Returns the size of an arraylist (number of elements).
+ */
+
+int
+arraylist_size (struct t_arraylist *arraylist)
+{
+ if (!arraylist)
+ return 0;
+
+ return arraylist->size;
+}
+
+/*
+ * Returns the pointer to an arraylist element, by index.
+ */
+
+void *
+arraylist_get (struct t_arraylist *arraylist, int index)
+{
+ if (!arraylist || (index < 0) || (index >= arraylist->size))
+ return NULL;
+
+ return arraylist->data[index];
+}
+
+/*
+ * Adjusts the allocated size of arraylist to add one element (if needed),
+ * so that the list has enough allocated data to store (current_size + 1)
+ * elements.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+arraylist_grow (struct t_arraylist *arraylist)
+{
+ int new_size_alloc;
+ void **data;
+
+ if (!arraylist)
+ return 0;
+
+ /* if we have enough space allocated, do nothing */
+ if (arraylist->size + 1 <= arraylist->size_alloc)
+ return 1;
+
+ new_size_alloc = (arraylist->size_alloc < 2) ?
+ 2 : arraylist->size_alloc + (arraylist->size_alloc / 2);
+
+ data = realloc (arraylist->data,
+ new_size_alloc * sizeof (*arraylist->data));
+ if (!data)
+ return 0;
+ arraylist->data = data;
+ memset (&arraylist->data[arraylist->size_alloc],
+ 0,
+ (new_size_alloc - arraylist->size_alloc) *
+ sizeof (*arraylist->data));
+ arraylist->size_alloc = new_size_alloc;
+
+ return 1;
+}
+
+/*
+ * Adjusts the allocated size of arraylist to remove one element (if needed),
+ * so that the list has enough allocated data to store (current size - 1)
+ * elements.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+arraylist_shrink (struct t_arraylist *arraylist)
+{
+ int new_size_alloc;
+ void **data;
+
+ if (!arraylist)
+ return 0;
+
+ /* we don't shrink if we are below the min allocated size */
+ if ((arraylist->size_alloc == 0)
+ || (arraylist->size_alloc <= arraylist->size_alloc_min))
+ {
+ return 1;
+ }
+
+ /* clear the arraylist if current allocated size is 1 */
+ if (arraylist->size_alloc == 1)
+ {
+ free (arraylist->data);
+ arraylist->data = NULL;
+ arraylist->size_alloc = 0;
+ return 1;
+ }
+
+ new_size_alloc = arraylist->size_alloc - (arraylist->size_alloc / 2);
+
+ if (arraylist->size - 1 >= new_size_alloc)
+ return 1;
+
+ data = realloc (arraylist->data,
+ new_size_alloc * sizeof (*arraylist->data));
+ if (!data)
+ return 0;
+ arraylist->data = data;
+ arraylist->size_alloc = new_size_alloc;
+
+ return 1;
+}
+
+/*
+ * Performs a binary search in the arraylist to find an element
+ * (this function must be called only if the arraylist is sorted).
+ *
+ * If "index" is not NULL, it is set with the index of element found (or -1 if
+ * element was not found).
+ *
+ * If "index_insert" is not NULL, it is set with the index that must be used to
+ * insert the element in the arraylist (to keep arraylist sorted).
+ *
+ * Returns pointer to element found, NULL if not found.
+ */
+
+void *
+arraylist_binary_search (struct t_arraylist *arraylist, void *pointer,
+ int *index, int *index_insert)
+{
+ int ret_index, ret_index_insert, start, end, middle, rc;
+ void *ret_pointer;
+
+ ret_index = -1;
+ ret_index_insert = -1;
+ ret_pointer = NULL;
+
+ if (!arraylist)
+ goto end;
+
+ start = 0;
+ end = arraylist->size - 1;
+
+ /*
+ * statistically we often add at the end, or before first element, so
+ * first check these cases (for performance), before doing the binary
+ * search
+ */
+ rc = (arraylist->callback_cmp) (arraylist->callback_cmp_data,
+ arraylist,
+ pointer,
+ arraylist->data[end]);
+ if (rc == 0)
+ {
+ ret_index = end;
+ /* by convention, add an element with same value after the last one */
+ ret_index_insert = -1;
+ ret_pointer = arraylist->data[end];
+ goto end;
+ }
+ if (rc > 0)
+ {
+ ret_index = -1;
+ ret_index_insert = -1;
+ ret_pointer = NULL;
+ goto end;
+ }
+ if (arraylist->size == 1)
+ {
+ ret_index = -1;
+ ret_index_insert = 0;
+ ret_pointer = NULL;
+ goto end;
+ }
+
+ rc = (arraylist->callback_cmp) (arraylist->callback_cmp_data,
+ arraylist,
+ pointer,
+ arraylist->data[start]);
+ if (rc == 0)
+ {
+ ret_index = start;
+ ret_index_insert = start;
+ ret_pointer = arraylist->data[start];
+ goto end;
+ }
+ if (rc < 0)
+ {
+ ret_index = -1;
+ ret_index_insert = start;
+ ret_pointer = NULL;
+ goto end;
+ }
+ if (arraylist->size == 2)
+ {
+ ret_index = -1;
+ ret_index_insert = end;
+ ret_pointer = NULL;
+ goto end;
+ }
+
+ start++;
+ end--;
+
+ /* perform a binary search to find the index */
+ while (start <= end)
+ {
+ middle = (start + end) / 2;
+
+ rc = (arraylist->callback_cmp) (arraylist->callback_cmp_data,
+ arraylist,
+ pointer,
+ arraylist->data[middle]);
+ if (rc == 0)
+ {
+ ret_index = middle;
+ ret_index_insert = middle;
+ ret_pointer = arraylist->data[middle];
+ goto end;
+ }
+
+ if (rc < 0)
+ end = middle - 1;
+ else
+ start = middle + 1;
+
+ if (start > end)
+ {
+ ret_index = -1;
+ ret_index_insert = (rc < 0) ? middle : middle + 1;
+ ret_pointer = NULL;
+ }
+ }
+
+end:
+ if (index)
+ *index = ret_index;
+ if (index_insert)
+ *index_insert = ret_index_insert;
+ return ret_pointer;
+}
+
+/*
+ * Performs a standard search in the arraylist to find an element
+ * (this function must be called only if the arraylist is NOT sorted).
+ *
+ * If "index" is not NULL, it is set with the index of element found (or -1 if
+ * element was not found).
+ *
+ * If "index_insert" is not NULL, it is set to -1 (elements are always added
+ * at the end of list when it is not sorted).
+ *
+ * Returns pointer to element found, NULL if not found.
+ */
+
+void *
+arraylist_standard_search (struct t_arraylist *arraylist, void *pointer,
+ int *index, int *index_insert)
+{
+ int i;
+
+ if (!arraylist)
+ goto end;
+
+ for (i = 0; i < arraylist->size; i++)
+ {
+ if ((arraylist->callback_cmp) (arraylist->callback_cmp_data,
+ arraylist, arraylist->data[i],
+ pointer) == 0)
+ {
+ if (index)
+ *index = i;
+ if (index_insert)
+ *index_insert = -1;
+ return arraylist->data[i];
+ }
+ }
+
+end:
+ if (index)
+ *index = -1;
+ if (index_insert)
+ *index_insert = -1;
+ return NULL;
+}
+
+/*
+ * Searches an element in the arraylist.
+ *
+ * If "index" is not NULL, it is set with the index of element found (or -1 if
+ * element was not found).
+ *
+ * If "index_insert" is not NULL, it is set with the index that must be used to
+ * insert the element in the arraylist (to keep arraylist sorted).
+ *
+ * Returns pointer to element found, NULL if not found.
+ */
+
+void *
+arraylist_search (struct t_arraylist *arraylist, void *pointer,
+ int *index, int *index_insert)
+{
+ if (index)
+ *index = -1;
+ if (index_insert)
+ *index_insert = -1;
+
+ if (!arraylist || (arraylist->size == 0))
+ return NULL;
+
+ if (arraylist->sorted)
+ {
+ return arraylist_binary_search (arraylist, pointer,
+ index, index_insert);
+ }
+ else
+ {
+ return arraylist_standard_search (arraylist, pointer,
+ index, index_insert);
+ }
+}
+
+/*
+ * Inserts an element at a given index (and shifts next elements by one
+ * position), or at automatic index if the arraylist is sorted.
+ *
+ * If the index is negative and that the arraylist is not sorted, the element
+ * is added at the end of arraylist.
+ *
+ * If the arraylist is sorted, the argument "index" is ignored (the element
+ * will be inserted at appropriate position, to keep arraylist sorted).
+ *
+ * Returns the index of the new element (>= 0) or -1 if error.
+ */
+
+int
+arraylist_insert (struct t_arraylist *arraylist, int index, void *pointer)
+{
+ int index_insert, i;
+
+ if (!arraylist)
+ return -1;
+
+ if (arraylist->sorted)
+ {
+ (void) arraylist_search (arraylist, pointer, &index, &index_insert);
+ if ((index >= 0) && !arraylist->allow_duplicates)
+ {
+ while ((index < arraylist->size)
+ && (((arraylist->callback_cmp) (arraylist->callback_cmp_data,
+ arraylist, arraylist->data[index],
+ pointer)) == 0))
+ {
+ if (arraylist->callback_free)
+ {
+ (arraylist->callback_free) (arraylist->callback_free_data,
+ arraylist,
+ arraylist->data[index]);
+ }
+ arraylist_remove (arraylist, index);
+ }
+ }
+ else
+ index = index_insert;
+ }
+ else if (!arraylist->allow_duplicates)
+ {
+ /*
+ * arraylist is not sorted and does not allow duplicates, then we
+ * remove any element with the same value
+ */
+ i = 0;
+ while (i < arraylist->size)
+ {
+ if ((arraylist->callback_cmp) (arraylist->callback_cmp_data,
+ arraylist, arraylist->data[i],
+ pointer) == 0)
+ {
+ if (arraylist->callback_free)
+ {
+ (arraylist->callback_free) (arraylist->callback_free_data,
+ arraylist,
+ arraylist->data[i]);
+ }
+ arraylist_remove (arraylist, i);
+ }
+ else
+ i++;
+ }
+ }
+
+ /* if index is negative or too big, add at the end */
+ if ((index < 0) || (index > arraylist->size))
+ index = arraylist->size;
+
+ if (!arraylist_grow (arraylist))
+ return -1;
+
+ /* shift next elements by one position */
+ if (index < arraylist->size)
+ {
+ memmove (&arraylist->data[index + 1],
+ &arraylist->data[index],
+ (arraylist->size - index) * sizeof (*arraylist->data));
+ }
+
+ /* set element */
+ arraylist->data[index] = pointer;
+
+ (arraylist->size)++;
+
+ return index;
+}
+
+/*
+ * Adds an element at the end of arraylist (or in the middle if the arraylist
+ * is sorted).
+ *
+ * Returns the index of the new element (>= 0) or -1 if error.
+ */
+
+int
+arraylist_add (struct t_arraylist *arraylist, void *pointer)
+{
+ if (!arraylist)
+ return -1;
+
+ return arraylist_insert (arraylist, -1, pointer);
+}
+
+/*
+ * Removes one element from the arraylist.
+ *
+ * Returns the index removed or -1 if error.
+ */
+
+int
+arraylist_remove (struct t_arraylist *arraylist, int index)
+{
+ if (!arraylist || (index < 0) || (index >= arraylist->size))
+ return -1;
+
+ if (index < arraylist->size - 1)
+ {
+ memmove (&arraylist->data[index],
+ &arraylist->data[index + 1],
+ (arraylist->size - index - 1) * sizeof (*arraylist->data));
+ memset (&arraylist->data[arraylist->size - 1], 0,
+ sizeof (*arraylist->data));
+ }
+ else
+ {
+ memset (&arraylist->data[index], 0, sizeof (*arraylist->data));
+ }
+
+ arraylist_shrink (arraylist);
+
+ (arraylist->size)--;
+
+ return index;
+}
+
+/*
+ * Removes all elements in the arraylist.
+ *
+ * Returns:
+ * 1: OK
+ * 0: error
+ */
+
+int
+arraylist_clear (struct t_arraylist *arraylist)
+{
+ if (!arraylist)
+ return 0;
+
+ if (arraylist->data
+ && (arraylist->size_alloc != arraylist->size_alloc_min))
+ {
+ free (arraylist->data);
+ arraylist->data = NULL;
+ arraylist->size_alloc = 0;
+ if (arraylist->size_alloc_min > 0)
+ {
+ arraylist->data = calloc(arraylist->size_alloc_min,
+ sizeof (*arraylist->data));
+ if (!arraylist->data)
+ return 0;
+ arraylist->size_alloc = arraylist->size_alloc_min;
+ }
+ }
+
+ arraylist->size = 0;
+
+ return 1;
+}
+
+/*
+ * Frees an arraylist.
+ */
+
+void
+arraylist_free (struct t_arraylist *arraylist)
+{
+ if (!arraylist)
+ return;
+
+ if (arraylist->data)
+ free (arraylist->data);
+
+ free (arraylist);
+}
+
+/*
+ * Prints an arraylist in WeeChat log file (usually for crash dump).
+ */
+
+void
+arraylist_print_log (struct t_arraylist *arraylist, const char *name)
+{
+ int i;
+
+ log_printf ("");
+ log_printf ("[arraylist %s (addr:0x%lx)]", name, arraylist);
+ log_printf (" size . . . . . . . . . : %d", arraylist->size);
+ log_printf (" size_alloc . . . . . . : %d", arraylist->size_alloc);
+ log_printf (" size_alloc_min . . . . : %d", arraylist->size_alloc_min);
+ log_printf (" sorted . . . . . . . . : %d", arraylist->sorted);
+ log_printf (" allow_duplicates . . . : %d", arraylist->allow_duplicates);
+ log_printf (" data . . . . . . . . . : 0x%lx", arraylist->data);
+ if (arraylist->data)
+ {
+ for (i = 0; i < arraylist->size_alloc; i++)
+ {
+ log_printf (" data[%08d] . . . : 0x%lx",
+ i, arraylist->data[i]);
+ }
+ }
+ log_printf (" callback_cmp . . . . . : 0x%lx", arraylist->callback_cmp);
+ log_printf (" callback_cmp_data. . . : 0x%lx", arraylist->callback_cmp_data);
+ log_printf (" callback_free. . . . . : 0x%lx", arraylist->callback_free);
+ log_printf (" callback_free_data . . : 0x%lx", arraylist->callback_free_data);
+}
diff --git a/src/core/wee-arraylist.h b/src/core/wee-arraylist.h
new file mode 100644
index 000000000..9f3ac481b
--- /dev/null
+++ b/src/core/wee-arraylist.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * WeeChat is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef WEECHAT_ARRAYLIST_H
+#define WEECHAT_ARRAYLIST_H 1
+
+struct t_arraylist;
+
+typedef int (t_arraylist_cmp)(void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2);
+typedef void (t_arraylist_free)(void *data, struct t_arraylist *arraylist,
+ void *pointer);
+
+struct t_arraylist
+{
+ int size; /* number of items in data */
+ int size_alloc; /* number of allocated items */
+ int size_alloc_min; /* min number of allocated items */
+ int sorted; /* 1 if the arraylist is sorted */
+ int allow_duplicates; /* 1 if duplicates are allowed */
+ void **data; /* pointers to data */
+ t_arraylist_cmp *callback_cmp; /* compare two elements */
+ void *callback_cmp_data; /* data for compare callback */
+ t_arraylist_free *callback_free; /* free an element */
+ void *callback_free_data; /* data for free callback */
+};
+
+extern struct t_arraylist *arraylist_new (int initial_size,
+ int sorted,
+ int allow_duplicates,
+ t_arraylist_cmp *callback_cmp,
+ void *callback_cmp_data,
+ t_arraylist_free *callback_free,
+ void *callback_free_data);
+extern int arraylist_size (struct t_arraylist *arraylist);
+extern void *arraylist_get (struct t_arraylist *arraylist, int index);
+extern void *arraylist_search (struct t_arraylist *arraylist, void *pointer,
+ int *index, int *index_insert);
+extern int arraylist_insert (struct t_arraylist *arraylist, int index,
+ void *pointer);
+extern int arraylist_add (struct t_arraylist *arraylist, void *pointer);
+extern int arraylist_remove (struct t_arraylist *arraylist, int index);
+extern int arraylist_clear (struct t_arraylist *arraylist);
+extern void arraylist_free (struct t_arraylist *arraylist);
+extern void arraylist_print_log (struct t_arraylist *arraylist,
+ const char *name);
+
+#endif /* WEECHAT_ARRAYLIST_H */
diff --git a/src/core/wee-completion.c b/src/core/wee-completion.c
index d4b77127d..e97c2d823 100644
--- a/src/core/wee-completion.c
+++ b/src/core/wee-completion.c
@@ -34,6 +34,7 @@
#include <unistd.h>
#include "weechat.h"
+#include "wee-arraylist.h"
#include "wee-config.h"
#include "wee-hashtable.h"
#include "wee-hook.h"
@@ -625,12 +626,12 @@ completion_list_add_nicks_cb (void *data,
(void) completion_item;
(void) buffer;
- count_before = weelist_size (completion->completion_list);
+ count_before = completion->list->size;
hook_completion_exec (completion->buffer->plugin,
"nick",
completion->buffer,
completion);
- if (weelist_size (completion->completion_list) == count_before)
+ if (completion->list->size == count_before)
{
/*
* no plugin overrides nick completion => use default nick
diff --git a/src/gui/gui-bar-item.c b/src/gui/gui-bar-item.c
index 9c3d6129a..9fddb9658 100644
--- a/src/gui/gui-bar-item.c
+++ b/src/gui/gui-bar-item.c
@@ -29,6 +29,7 @@
#include <time.h>
#include "../core/weechat.h"
+#include "../core/wee-arraylist.h"
#include "../core/wee-config.h"
#include "../core/wee-hashtable.h"
#include "../core/wee-hdata.h"
@@ -1484,9 +1485,9 @@ gui_bar_item_default_completion (void *data, struct t_gui_bar_item *item,
struct t_gui_buffer *buffer,
struct t_hashtable *extra_info)
{
- int length;
+ int length, i;
char *buf, str_number[64];
- struct t_gui_completion_partial *ptr_item;
+ struct t_gui_completion_word *ptr_completion_word;
/* make C compiler happy */
(void) data;
@@ -1495,37 +1496,39 @@ gui_bar_item_default_completion (void *data, struct t_gui_bar_item *item,
(void) extra_info;
if (!buffer || !buffer->completion
- || !buffer->completion->partial_completion_list)
+ || (buffer->completion->partial_list->size == 0))
{
return NULL;
}
length = 1;
- for (ptr_item = buffer->completion->partial_completion_list;
- ptr_item; ptr_item = ptr_item->next_item)
+ for (i = 0; i < buffer->completion->partial_list->size; i++)
{
- length += strlen (ptr_item->word) + 32;
+ ptr_completion_word =
+ (struct t_gui_completion_word *)(buffer->completion->partial_list->data[i]);
+ length += strlen (ptr_completion_word->word) + 32;
}
buf = malloc (length);
if (buf)
{
buf[0] = '\0';
- for (ptr_item = buffer->completion->partial_completion_list;
- ptr_item; ptr_item = ptr_item->next_item)
+ for (i = 0; i < buffer->completion->partial_list->size; i++)
{
+ ptr_completion_word =
+ (struct t_gui_completion_word *)(buffer->completion->partial_list->data[i]);
strcat (buf, GUI_COLOR_CUSTOM_BAR_FG);
- strcat (buf, ptr_item->word);
- if (ptr_item->count > 0)
+ strcat (buf, ptr_completion_word->word);
+ if (ptr_completion_word->count > 0)
{
strcat (buf, GUI_COLOR_CUSTOM_BAR_DELIM);
strcat (buf, "(");
snprintf (str_number, sizeof (str_number),
- "%d", ptr_item->count);
+ "%d", ptr_completion_word->count);
strcat (buf, str_number);
strcat (buf, ")");
}
- if (ptr_item->next_item)
+ if (i < buffer->completion->partial_list->size - 1)
strcat (buf, " ");
}
}
diff --git a/src/gui/gui-completion.c b/src/gui/gui-completion.c
index eee784bca..7a416eee3 100644
--- a/src/gui/gui-completion.c
+++ b/src/gui/gui-completion.c
@@ -33,6 +33,7 @@
#include <unistd.h>
#include "../core/weechat.h"
+#include "../core/wee-arraylist.h"
#include "../core/wee-completion.h"
#include "../core/wee-config.h"
#include "../core/wee-hdata.h"
@@ -51,6 +52,48 @@ int gui_completion_freeze = 0; /* 1 to freeze completions (do not */
/*
+ * Compares two words in completion list.
+ */
+
+int
+gui_completion_word_compare_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ struct t_gui_completion_word *completion_word1, *completion_word2;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ completion_word1 = (struct t_gui_completion_word *)pointer1;
+ completion_word2 = (struct t_gui_completion_word *)pointer2;
+
+ return string_strcasecmp (completion_word1->word, completion_word2->word);
+}
+
+/*
+ * Frees a word in completion list.
+ */
+
+void
+gui_completion_word_free_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer)
+{
+ struct t_gui_completion_word *completion_word;
+
+ /* make C compiler happy */
+ (void) data;
+ (void) arraylist;
+
+ completion_word = (struct t_gui_completion_word *)pointer;
+
+ if (completion_word->word)
+ free (completion_word->word);
+
+ free (completion_word);
+}
+
+/*
* Initializes completion for a buffer.
*/
@@ -70,7 +113,9 @@ gui_completion_buffer_init (struct t_gui_completion *completion,
completion->add_space = 1;
completion->force_partial_completion = 0;
- completion->completion_list = weelist_new ();
+ completion->list = arraylist_new (32, 1, 0,
+ &gui_completion_word_compare_cb, NULL,
+ &gui_completion_word_free_cb, NULL);
completion->word_found = NULL;
completion->word_found_is_nick = 0;
@@ -78,8 +123,10 @@ gui_completion_buffer_init (struct t_gui_completion *completion,
completion->diff_size = 0;
completion->diff_length = 0;
- completion->partial_completion_list = NULL;
- completion->last_partial_completion = NULL;
+ completion->partial_list = arraylist_new (
+ 0, 0, 0,
+ &gui_completion_word_compare_cb, NULL,
+ &gui_completion_word_free_cb, NULL);
}
/*
@@ -88,66 +135,23 @@ gui_completion_buffer_init (struct t_gui_completion *completion,
* Returns pointer to new item, NULL if error.
*/
-struct t_gui_completion_partial *
+struct t_gui_completion_word *
gui_completion_partial_list_add (struct t_gui_completion *completion,
const char *word, int count)
{
- struct t_gui_completion_partial *new_item;
+ struct t_gui_completion_word *new_completion_word;
- new_item = malloc (sizeof (*new_item));
- if (new_item)
+ new_completion_word = malloc (sizeof (*new_completion_word));
+ if (new_completion_word)
{
- new_item->word = strdup (word);
- new_item->count = count;
+ new_completion_word->word = strdup (word);
+ new_completion_word->nick_completion = 0;
+ new_completion_word->count = count;
- new_item->prev_item = completion->last_partial_completion;
- if (completion->partial_completion_list)
- (completion->last_partial_completion)->next_item = new_item;
- else
- completion->partial_completion_list = new_item;
- completion->last_partial_completion = new_item;
- new_item->next_item = NULL;
+ arraylist_add (completion->partial_list, new_completion_word);
}
- return new_item;
-}
-
-/*
- * Removes an item from partial completion list.
- */
-
-void
-gui_completion_partial_list_free (struct t_gui_completion *completion,
- struct t_gui_completion_partial *item)
-{
- /* remove partial completion item from list */
- if (item->prev_item)
- (item->prev_item)->next_item = item->next_item;
- if (item->next_item)
- (item->next_item)->prev_item = item->prev_item;
- if (completion->partial_completion_list == item)
- completion->partial_completion_list = item->next_item;
- if (completion->last_partial_completion == item)
- completion->last_partial_completion = item->prev_item;
-
- /* free data */
- if (item->word)
- free (item->word);
-
- free (item);
-}
-
-/*
- * Removes partial completion list.
- */
-void
-gui_completion_partial_list_free_all (struct t_gui_completion *completion)
-{
- while (completion->partial_completion_list)
- {
- gui_completion_partial_list_free (completion,
- completion->partial_completion_list);
- }
+ return new_completion_word;
}
/*
@@ -169,17 +173,17 @@ gui_completion_free_data (struct t_gui_completion *completion)
free (completion->args);
completion->args = NULL;
- if (completion->completion_list)
+ if (completion->list)
{
- weelist_free (completion->completion_list);
- completion->completion_list = NULL;
+ arraylist_free (completion->list);
+ completion->list = NULL;
}
if (completion->word_found)
free (completion->word_found);
completion->word_found = NULL;
- gui_completion_partial_list_free_all (completion);
+ arraylist_clear (completion->partial_list);
}
/*
@@ -206,9 +210,9 @@ gui_completion_stop (struct t_gui_completion *completion)
completion->context = GUI_COMPLETION_NULL;
completion->position = -1;
- if (completion->partial_completion_list)
+ if (completion->partial_list->size > 0)
{
- gui_completion_partial_list_free_all (completion);
+ arraylist_clear (completion->partial_list);
(void) hook_signal_send ("partial_completion",
WEECHAT_HOOK_SIGNAL_STRING, NULL);
}
@@ -369,7 +373,9 @@ void
gui_completion_list_add (struct t_gui_completion *completion, const char *word,
int nick_completion, const char *where)
{
+ struct t_gui_completion_word *completion_word;
char buffer[512];
+ int index;
if (!word || !word[0])
return;
@@ -380,17 +386,37 @@ gui_completion_list_add (struct t_gui_completion *completion, const char *word,
|| (!nick_completion && (string_strncasecmp (completion->base_word, word,
utf8_strlen (completion->base_word)) == 0)))
{
- if (nick_completion && (completion->base_word_pos == 0))
- {
- snprintf (buffer, sizeof (buffer), "%s%s",
- word, CONFIG_STRING(config_completion_nick_completer));
- weelist_add (completion->completion_list, buffer, where,
- (nick_completion) ? (void *)1 : (void *)0);
- }
- else
+ completion_word = malloc (sizeof (*completion_word));
+ if (completion_word)
{
- weelist_add (completion->completion_list, word, where,
- (nick_completion) ? (void *)1 : (void *)0);
+ completion_word->nick_completion = nick_completion;
+ completion_word->count = 0;
+
+ index = -1;
+ if (strcmp (where, WEECHAT_LIST_POS_BEGINNING) == 0)
+ {
+ completion->list->sorted = 0;
+ index = 0;
+ }
+ else if (strcmp (where, WEECHAT_LIST_POS_END) == 0)
+ {
+ completion->list->sorted = 0;
+ index = -1;
+ }
+
+ if (nick_completion && (completion->base_word_pos == 0))
+ {
+ snprintf (buffer, sizeof (buffer), "%s%s",
+ word,
+ CONFIG_STRING(config_completion_nick_completer));
+ completion_word->word = strdup (buffer);
+ arraylist_insert (completion->list, index, completion_word);
+ }
+ else
+ {
+ completion_word->word = strdup (word);
+ arraylist_insert (completion->list, index, completion_word);
+ }
}
}
}
@@ -842,28 +868,32 @@ gui_completion_find_context (struct t_gui_completion *completion,
*/
int
-gui_completion_common_prefix_size (struct t_weelist *list,
+gui_completion_common_prefix_size (struct t_arraylist *list,
const char *utf_char)
{
- struct t_weelist_item *ptr_item;
char *ptr_first_item, *ptr_char, *next_char;
+ struct t_gui_completion_word *ptr_completion_word;
+ int i;
- ptr_first_item = list->items->data;
+ ptr_first_item = ((struct t_gui_completion_word *)(list->data[0]))->word;
ptr_char = ptr_first_item;
while (ptr_char && ptr_char[0])
{
next_char = utf8_next_char (ptr_char);
- for (ptr_item = list->items->next_item; ptr_item;
- ptr_item = ptr_item->next_item)
+ for (i = 1; i < list->size; i++)
{
+ ptr_completion_word =
+ (struct t_gui_completion_word *)(list->data[i]);
if (!utf_char
- || (utf8_charcasecmp (utf_char, ptr_item->data) == 0))
+ || (utf8_charcasecmp (utf_char,
+ ptr_completion_word->word) == 0))
{
- if ((ptr_item->data[ptr_char - ptr_first_item] == '\0')
- || (utf8_charcasecmp (ptr_char,
- ptr_item->data + (ptr_char - ptr_first_item)) != 0))
+ if ((ptr_completion_word->word[ptr_char - ptr_first_item] == '\0')
+ || (utf8_charcasecmp (
+ ptr_char,
+ ptr_completion_word->word + (ptr_char - ptr_first_item)) != 0))
{
return ptr_char - ptr_first_item;
}
@@ -883,65 +913,78 @@ void
gui_completion_partial_build_list (struct t_gui_completion *completion,
int common_prefix_size)
{
- int char_size, items_count;
+ int i, char_size, items_count, index;
char utf_char[16], *word;
- struct t_weelist *weelist_temp;
- struct t_weelist_item *ptr_item, *next_item;
+ struct t_gui_completion_word *ptr_completion_word, *new_completion_word;
+ struct t_arraylist *list_temp;
- gui_completion_partial_list_free_all (completion);
+ arraylist_clear (completion->partial_list);
- if (!completion->completion_list || !completion->completion_list->items)
+ if (!completion->list || (completion->list->size == 0))
return;
- weelist_temp = weelist_new ();
- if (!weelist_temp)
+ list_temp = arraylist_new (completion->list->size, 1, 0,
+ &gui_completion_word_compare_cb, NULL,
+ &gui_completion_word_free_cb, NULL);
+ if (!list_temp)
return;
- for (ptr_item = completion->completion_list->items; ptr_item;
- ptr_item = ptr_item->next_item)
+ for (i = 0; i < completion->list->size; i++)
{
- weelist_add (weelist_temp, ptr_item->data + common_prefix_size,
- WEECHAT_LIST_POS_END, NULL);
+ ptr_completion_word =
+ (struct t_gui_completion_word *)completion->list->data[i];
+ new_completion_word = malloc (sizeof (*new_completion_word));
+ if (new_completion_word)
+ {
+ new_completion_word->word = strdup (
+ ptr_completion_word->word + common_prefix_size);
+ new_completion_word->nick_completion = 0;
+ new_completion_word->count = 0;
+ arraylist_add (list_temp, new_completion_word);
+ }
}
- while (weelist_temp->items)
+ while (list_temp->size > 0)
{
- char_size = utf8_char_size (weelist_temp->items->data);
- memcpy (utf_char, weelist_temp->items->data, char_size);
+ ptr_completion_word =
+ (struct t_gui_completion_word *)list_temp->data[0];
+ char_size = utf8_char_size (ptr_completion_word->word);
+ memcpy (utf_char, ptr_completion_word->word, char_size);
utf_char[char_size] = '\0';
word = NULL;
- common_prefix_size = gui_completion_common_prefix_size (weelist_temp,
+ common_prefix_size = gui_completion_common_prefix_size (list_temp,
utf_char);
if (common_prefix_size > 0)
{
- word = string_strndup (weelist_temp->items->data,
+ word = string_strndup (ptr_completion_word->word,
common_prefix_size);
}
items_count = 0;
- ptr_item = weelist_temp->items;
- while (ptr_item)
+ index = 0;
+ while (index < list_temp->size)
{
- next_item = ptr_item->next_item;
-
- if (utf8_charcasecmp (utf_char, ptr_item->data) == 0)
+ ptr_completion_word =
+ (struct t_gui_completion_word *)list_temp->data[index];
+ if (utf8_charcasecmp (utf_char, ptr_completion_word->word) == 0)
{
- weelist_remove (weelist_temp, ptr_item);
+ arraylist_remove (list_temp, index);
items_count++;
}
-
- ptr_item = next_item;
+ else
+ index++;
}
if (word)
{
- gui_completion_partial_list_add (completion,
- word,
- CONFIG_BOOLEAN(config_completion_partial_completion_count) ?
- items_count : -1);
+ gui_completion_partial_list_add (
+ completion,
+ word,
+ CONFIG_BOOLEAN(config_completion_partial_completion_count) ?
+ items_count : -1);
free (word);
}
}
- weelist_free (weelist_temp);
+ arraylist_free (list_temp);
}
/*
@@ -952,8 +995,8 @@ void
gui_completion_complete (struct t_gui_completion *completion)
{
int length, word_found_seen, other_completion, partial_completion;
- int common_prefix_size, item_is_nick;
- struct t_weelist_item *ptr_item, *ptr_item2;
+ int common_prefix_size, index, index2;
+ struct t_gui_completion_word *ptr_completion_word, *ptr_completion_word2;
length = utf8_strlen (completion->base_word);
word_found_seen = 0;
@@ -977,21 +1020,12 @@ gui_completion_complete (struct t_gui_completion *completion)
common_prefix_size = 0;
if (partial_completion
- && completion->completion_list && completion->completion_list->items)
+ && completion->list && (completion->list->size > 0))
{
- common_prefix_size = gui_completion_common_prefix_size (completion->completion_list,
+ common_prefix_size = gui_completion_common_prefix_size (completion->list,
NULL);
}
- ptr_item = NULL;
- if (completion->completion_list)
- {
- if (completion->direction < 0)
- ptr_item = completion->completion_list->last_item;
- else
- ptr_item = completion->completion_list->items;
- }
-
if (partial_completion
&& completion->word_found
&& (utf8_strlen (completion->word_found) >= common_prefix_size))
@@ -999,59 +1033,68 @@ gui_completion_complete (struct t_gui_completion *completion)
return;
}
- while (ptr_item)
+ index = -1;
+ if (completion->list)
+ {
+ if (completion->direction < 0)
+ index = completion->list->size - 1;
+ else
+ index = 0;
+ }
+
+ while ((index >= 0) && (index < completion->list->size))
{
- item_is_nick = ((long)(ptr_item->user_data) == 1);
- if ((item_is_nick
- && (gui_completion_nickncmp (completion->base_word, ptr_item->data,
+ ptr_completion_word =
+ (struct t_gui_completion_word *)(completion->list->data[index]);
+ if ((ptr_completion_word->nick_completion
+ && (gui_completion_nickncmp (completion->base_word,
+ ptr_completion_word->word,
length) == 0))
- || ((!item_is_nick)
- && (string_strncasecmp (completion->base_word, ptr_item->data,
+ || (!ptr_completion_word->nick_completion
+ && (string_strncasecmp (completion->base_word,
+ ptr_completion_word->word,
length) == 0)))
{
if ((!completion->word_found) || word_found_seen)
{
if (completion->word_found)
free (completion->word_found);
- completion->word_found = strdup (ptr_item->data);
- completion->word_found_is_nick = item_is_nick;
- if (item_is_nick
+ completion->word_found = strdup (ptr_completion_word->word);
+ completion->word_found_is_nick =
+ ptr_completion_word->nick_completion;
+ if (ptr_completion_word->nick_completion
&& !CONFIG_BOOLEAN(config_completion_nick_add_space))
{
completion->add_space = 0;
}
/* stop after first nick if user asked that */
- if (item_is_nick
+ if (ptr_completion_word->nick_completion
&& CONFIG_BOOLEAN(config_completion_nick_first_only))
{
gui_completion_stop (completion);
return;
}
- if (completion->direction < 0)
- ptr_item2 = ptr_item->prev_item;
- else
- ptr_item2 = ptr_item->next_item;
-
- while (ptr_item2)
+ index2 = (completion->direction < 0) ? index - 1 : index + 1;
+ while ((index2 >= 0) && (index2 < completion->list->size))
{
- if ((item_is_nick
+ ptr_completion_word2 =
+ (struct t_gui_completion_word *)(completion->list->data[index2]);
+ if ((ptr_completion_word->nick_completion
&& (gui_completion_nickncmp (completion->base_word,
- ptr_item2->data,
+ ptr_completion_word2->word,
length) == 0))
- || ((!item_is_nick)
+ || (!ptr_completion_word->nick_completion
&& (string_strncasecmp (completion->base_word,
- ptr_item2->data,
+ ptr_completion_word2->word,
length) == 0)))
{
other_completion++;
}
- if (completion->direction < 0)
- ptr_item2 = ptr_item2->prev_item;
- else
- ptr_item2 = ptr_item2->next_item;
+ index2 = (completion->direction < 0) ?
+ index2 - 1 : index2 + 1;
}
if (other_completion == 0)
@@ -1086,20 +1129,17 @@ gui_completion_complete (struct t_gui_completion *completion)
return;
}
- gui_completion_partial_list_free_all (completion);
+ arraylist_clear (completion->partial_list);
return;
}
other_completion++;
}
if (completion->word_found &&
- (strcmp (ptr_item->data, completion->word_found) == 0))
+ (strcmp (ptr_completion_word->word, completion->word_found) == 0))
word_found_seen = 1;
- if (completion->direction < 0)
- ptr_item = ptr_item->prev_item;
- else
- ptr_item = ptr_item->next_item;
+ index = (completion->direction < 0) ? index - 1 : index + 1;
}
/*
@@ -1124,7 +1164,7 @@ gui_completion_command (struct t_gui_completion *completion)
{
struct t_hook *ptr_hook;
- if (!completion->completion_list->items)
+ if (completion->list->size == 0)
{
for (ptr_hook = weechat_hooks[HOOK_TYPE_COMMAND]; ptr_hook;
ptr_hook = ptr_hook->next_hook)
@@ -1154,18 +1194,19 @@ gui_completion_auto (struct t_gui_completion *completion)
if ((completion->base_word[0] == '/')
|| (completion->base_word[0] == '~'))
{
- if (!completion->completion_list->items)
+ if (completion->list->size == 0)
completion_list_add_filename_cb (NULL, NULL, NULL, completion);
gui_completion_complete (completion);
return;
}
/* use default template completion */
- if (!completion->completion_list->items)
+ if (completion->list->size == 0)
{
- gui_completion_build_list_template (completion,
- CONFIG_STRING(config_completion_default_template),
- NULL);
+ gui_completion_build_list_template (
+ completion,
+ CONFIG_STRING(config_completion_default_template),
+ NULL);
}
gui_completion_complete (completion);
}
@@ -1205,7 +1246,7 @@ gui_completion_search (struct t_gui_completion *completion, int direction,
gui_completion_command (completion);
break;
case GUI_COMPLETION_COMMAND_ARG:
- if (completion->completion_list->items)
+ if (completion->list->size > 0)
gui_completion_complete (completion);
else
{
@@ -1286,24 +1327,23 @@ gui_completion_hdata_completion_cb (void *data, const char *hdata_name)
HDATA_VAR(struct t_gui_completion, direction, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_gui_completion, add_space, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_gui_completion, force_partial_completion, INTEGER, 0, NULL, NULL);
- HDATA_VAR(struct t_gui_completion, completion_list, POINTER, 0, NULL, NULL);
+ HDATA_VAR(struct t_gui_completion, list, POINTER, 0, NULL, NULL);
HDATA_VAR(struct t_gui_completion, word_found, STRING, 0, NULL, NULL);
HDATA_VAR(struct t_gui_completion, word_found_is_nick, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_gui_completion, position_replace, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_gui_completion, diff_size, INTEGER, 0, NULL, NULL);
HDATA_VAR(struct t_gui_completion, diff_length, INTEGER, 0, NULL, NULL);
- HDATA_VAR(struct t_gui_completion, partial_completion_list, POINTER, 0, NULL, "completion_partial");
- HDATA_VAR(struct t_gui_completion, last_partial_completion, POINTER, 0, NULL, "completion_partial");
+ HDATA_VAR(struct t_gui_completion, partial_list, POINTER, 0, NULL, NULL);
}
return hdata;
}
/*
- * Returns hdata for partial completion.
+ * Returns hdata for completion word.
*/
struct t_hdata *
-gui_completion_hdata_completion_partial_cb (void *data, const char *hdata_name)
+gui_completion_hdata_completion_word_cb (void *data, const char *hdata_name)
{
struct t_hdata *hdata;
@@ -1314,23 +1354,41 @@ gui_completion_hdata_completion_partial_cb (void *data, const char *hdata_name)
0, 0, NULL, NULL);
if (hdata)
{
- HDATA_VAR(struct t_gui_completion_partial, word, STRING, 0, NULL, NULL);
- HDATA_VAR(struct t_gui_completion_partial, count, INTEGER, 0, NULL, NULL);
- HDATA_VAR(struct t_gui_completion_partial, prev_item, POINTER, 0, NULL, hdata_name);
- HDATA_VAR(struct t_gui_completion_partial, next_item, POINTER, 0, NULL, hdata_name);
+ HDATA_VAR(struct t_gui_completion_word, word, STRING, 0, NULL, NULL);
+ HDATA_VAR(struct t_gui_completion_word, nick_completion, CHAR, 0, NULL, NULL);
+ HDATA_VAR(struct t_gui_completion_word, count, INTEGER, 0, NULL, NULL);
}
return hdata;
}
/*
+ * Prints list of completion words in WeeChat log file (usually for crash dump).
+ */
+
+void
+gui_completion_list_words_print_log (struct t_arraylist *list,
+ const char *name)
+{
+ int i;
+ struct t_gui_completion_word *ptr_completion_word;
+
+ for (i = 0; i < list->size; i++)
+ {
+ ptr_completion_word = (struct t_gui_completion_word *)(list->data[i]);
+ log_printf ("[%s (addr:0x%lx)]", name, ptr_completion_word);
+ log_printf (" word. . . . . . . . . . : '%s'", ptr_completion_word->word);
+ log_printf (" nicklist_completion . . : %d", ptr_completion_word->nick_completion);
+ log_printf (" count . . . . . . . . . : %d", ptr_completion_word->count);
+ }
+}
+
+/*
* Prints completion list in WeeChat log file (usually for crash dump).
*/
void
gui_completion_print_log (struct t_gui_completion *completion)
{
- struct t_gui_completion_partial *ptr_item;
-
log_printf ("[completion (addr:0x%lx)]", completion);
log_printf (" buffer. . . . . . . . . : 0x%lx", completion->buffer);
log_printf (" context . . . . . . . . : %d", completion->context);
@@ -1343,29 +1401,22 @@ gui_completion_print_log (struct t_gui_completion *completion)
log_printf (" direction . . . . . . . : %d", completion->direction);
log_printf (" add_space . . . . . . . : %d", completion->add_space);
log_printf (" force_partial_completion: %d", completion->force_partial_completion);
- log_printf (" completion_list . . . . : 0x%lx", completion->completion_list);
+ log_printf (" list. . . . . . . . . . : 0x%lx", completion->list);
log_printf (" word_found. . . . . . . : '%s'", completion->word_found);
log_printf (" word_found_is_nick. . . : %d", completion->word_found_is_nick);
log_printf (" position_replace. . . . : %d", completion->position_replace);
log_printf (" diff_size . . . . . . . : %d", completion->diff_size);
log_printf (" diff_length . . . . . . : %d", completion->diff_length);
- if (completion->completion_list)
+ if (completion->list)
{
log_printf ("");
- weelist_print_log (completion->completion_list,
- "completion list element");
+ gui_completion_list_words_print_log (completion->list,
+ "completion word");
}
- if (completion->partial_completion_list)
+ if (completion->partial_list)
{
log_printf ("");
- for (ptr_item = completion->partial_completion_list;
- ptr_item; ptr_item = ptr_item->next_item)
- {
- log_printf ("[partial completion item (addr:0x%lx)]", ptr_item);
- log_printf (" word. . . . . . . . . . : '%s'", ptr_item->word);
- log_printf (" count . . . . . . . . . : %d", ptr_item->count);
- log_printf (" prev_item . . . . . . . : 0x%lx", ptr_item->prev_item);
- log_printf (" next_item . . . . . . . : 0x%lx", ptr_item->next_item);
- }
+ arraylist_print_log (completion->partial_list,
+ "partial completion word");
}
}
diff --git a/src/gui/gui-completion.h b/src/gui/gui-completion.h
index 7192779de..7c1368937 100644
--- a/src/gui/gui-completion.h
+++ b/src/gui/gui-completion.h
@@ -25,12 +25,12 @@
#define GUI_COMPLETION_COMMAND_ARG 2
#define GUI_COMPLETION_AUTO 3
-struct t_gui_completion_partial
+struct t_gui_completion_word
{
- char *word; /* (partial) word matching completion */
+ char *word; /* word matching completion */
+ char nick_completion; /* 1 if it is completion of a nick */
int count; /* number of matching items with this word */
- struct t_gui_completion_partial *prev_item;
- struct t_gui_completion_partial *next_item;
+ /* (for partial completion) */
};
struct t_gui_completion
@@ -49,7 +49,7 @@ struct t_gui_completion
int force_partial_completion; /* force partial completion? */
/* for command argument completion */
- struct t_weelist *completion_list; /* data list for completion */
+ struct t_arraylist *list; /* data list for completion */
/* completion found */
char *word_found; /* word found (to replace base word) */
@@ -59,8 +59,7 @@ struct t_gui_completion
int diff_length; /* length difference (<= diff_size) */
/* partial completion */
- struct t_gui_completion_partial *partial_completion_list;
- struct t_gui_completion_partial *last_partial_completion;
+ struct t_arraylist *partial_list;
};
/* completion variables */
diff --git a/src/plugins/plugin-api.c b/src/plugins/plugin-api.c
index ca3c5ed5d..661939251 100644
--- a/src/plugins/plugin-api.c
+++ b/src/plugins/plugin-api.c
@@ -1298,8 +1298,6 @@ plugin_api_init ()
&gui_buffer_hdata_buffer_visited_cb, NULL);
hook_hdata (NULL, "completion", N_("structure with completion"),
&gui_completion_hdata_completion_cb, NULL);
- hook_hdata (NULL, "completion_partial", N_("structure with partial completion"),
- &gui_completion_hdata_completion_partial_cb, NULL);
hook_hdata (NULL, "config_file", N_("config file"),
&config_file_hdata_config_file_cb, NULL);
hook_hdata (NULL, "config_section", N_("config section"),
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 4a9a9132c..19ff1003e 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -30,6 +30,7 @@ include_directories(${CPPUTEST_INCLUDE_DIRS} ${PROJECT_BINARY_DIR} ${PROJECT_SOU
# unit tests
set(LIB_WEECHAT_UNIT_TESTS_SRC
+ unit/core/test-arraylist.cpp
unit/core/test-eval.cpp
unit/core/test-hashtable.cpp
unit/core/test-hdata.cpp
@@ -53,6 +54,8 @@ set(LIBS
${PROJECT_BINARY_DIR}/src/gui/curses/libweechat_gui_curses.a
${CMAKE_CURRENT_BINARY_DIR}/libweechat_ncurses_fake.a
${CMAKE_CURRENT_BINARY_DIR}/libweechat_unit_tests.a
+ # due to circular references, we must link two times with libweechat_core.a
+ ${PROJECT_BINARY_DIR}/src/core/libweechat_core.a
${EXTRA_LIBS}
${CURL_LIBRARIES}
${CPPUTEST_LIBRARIES})
diff --git a/tests/Makefile.am b/tests/Makefile.am
index e5b2c39a1..5e04c5122 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -23,7 +23,8 @@ noinst_LIBRARIES = lib_ncurses_fake.a lib_weechat_unit_tests.a
lib_ncurses_fake_a_SOURCES = ncurses-fake.c
-lib_weechat_unit_tests_a_SOURCES = unit/core/test-eval.cpp \
+lib_weechat_unit_tests_a_SOURCES = unit/core/test-arraylist.cpp \
+ unit/core/test-eval.cpp \
unit/core/test-hashtable.cpp \
unit/core/test-hdata.cpp \
unit/core/test-infolist.cpp \
@@ -35,15 +36,15 @@ lib_weechat_unit_tests_a_SOURCES = unit/core/test-eval.cpp \
noinst_PROGRAMS = tests
-# Because of a linker bug, we have to link 2 times with lib_weechat_core.a
+# Due to circular references, we must link two times with libweechat_core.a
# (and it must be 2 different path/names to be kept by linker)
tests_LDADD = ./../src/core/lib_weechat_core.a \
../src/plugins/lib_weechat_plugins.a \
../src/gui/lib_weechat_gui_common.a \
../src/gui/curses/lib_weechat_gui_curses.a \
- ../src/core/lib_weechat_core.a \
lib_ncurses_fake.a \
lib_weechat_unit_tests.a \
+ ../src/core/lib_weechat_core.a \
$(PLUGINS_LFLAGS) \
$(GCRYPT_LFLAGS) \
$(GNUTLS_LFLAGS) \
diff --git a/tests/tests.cpp b/tests/tests.cpp
index 95e3e9b33..f134fed03 100644
--- a/tests/tests.cpp
+++ b/tests/tests.cpp
@@ -47,6 +47,7 @@ extern "C"
#include "CppUTest/CommandLineTestRunner.h"
/* import tests from libs */
+IMPORT_TEST_GROUP(Arraylist);
IMPORT_TEST_GROUP(Eval);
IMPORT_TEST_GROUP(Hashtable);
IMPORT_TEST_GROUP(Hdata);
diff --git a/tests/unit/core/test-arraylist.cpp b/tests/unit/core/test-arraylist.cpp
new file mode 100644
index 000000000..4812dc6f7
--- /dev/null
+++ b/tests/unit/core/test-arraylist.cpp
@@ -0,0 +1,418 @@
+/*
+ * test-arraylist.cpp - test arraylist functions
+ *
+ * Copyright (C) 2014 Sébastien Helleu <flashcode@flashtux.org>
+ *
+ * This file is part of WeeChat, the extensible chat client.
+ *
+ * WeeChat is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * WeeChat is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with WeeChat. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "CppUTest/TestHarness.h"
+
+extern "C"
+{
+#include <string.h>
+#include "src/core/wee-arraylist.h"
+#include "src/core/wee-string.h"
+}
+
+#define TEST_ARRAYLIST_ADD(__result, __value) \
+ LONGS_EQUAL(__result, \
+ arraylist_add (arraylist, (void *)(__value)));
+
+TEST_GROUP(Arraylist)
+{
+};
+
+/*
+ * Test callback comparing two arraylist elements.
+ *
+ * Returns:
+ * -1: element(pointer1) < element(pointer2)
+ * 0: element(pointer1) == element(pointer2)
+ * 1: element(pointer1) > element(pointer2)
+ */
+
+int
+test_cmp_cb (void *data, struct t_arraylist *arraylist,
+ void *pointer1, void *pointer2)
+{
+ if (!pointer1 || !pointer2)
+ return (pointer1) ? 1 : ((pointer2) ? -1 : 0);
+
+ return string_strcasecmp ((const char *)pointer1, (const char *)pointer2);
+}
+
+void
+test_arraylist (int initial_size, int sorted, int allow_duplicates)
+{
+ struct t_arraylist *arraylist;
+ int i, index, index_insert, expected_pos;
+ const char *item_aaa = "aaa";
+ const char *item_abc = "abc";
+ const char *item_DEF = "DEF";
+ const char *item_def = "def";
+ const char *item_xxx = "xxx";
+
+ /* create arraylist */
+ arraylist = arraylist_new (initial_size,
+ sorted,
+ allow_duplicates,
+ &test_cmp_cb, NULL,
+ NULL, NULL);
+
+ /* check values after creation */
+ CHECK(arraylist);
+ LONGS_EQUAL(0, arraylist->size);
+ LONGS_EQUAL(initial_size, arraylist->size_alloc);
+ LONGS_EQUAL(initial_size, arraylist->size_alloc_min);
+ if (initial_size > 0)
+ {
+ CHECK(arraylist->data);
+ for (i = 0; i < initial_size; i++)
+ {
+ POINTERS_EQUAL(NULL, arraylist->data[i]);
+ }
+ }
+ else
+ {
+ POINTERS_EQUAL(NULL, arraylist->data);
+ }
+ LONGS_EQUAL(sorted, arraylist->sorted);
+ LONGS_EQUAL(allow_duplicates, arraylist->allow_duplicates);
+
+ /* check size */
+ LONGS_EQUAL(0, arraylist_size (arraylist));
+
+ /* get element (this should always fail, the list is empty!) */
+ POINTERS_EQUAL(NULL, arraylist_get (NULL, -1));
+ POINTERS_EQUAL(NULL, arraylist_get (NULL, 0));
+ POINTERS_EQUAL(NULL, arraylist_get (NULL, 1));
+ POINTERS_EQUAL(NULL, arraylist_get (arraylist, -1));
+ POINTERS_EQUAL(NULL, arraylist_get (arraylist, 0));
+ POINTERS_EQUAL(NULL, arraylist_get (arraylist, 1));
+
+ /* search element (this should always fail, the list is empty!) */
+ POINTERS_EQUAL(NULL, arraylist_search (NULL, NULL, NULL, NULL));
+ POINTERS_EQUAL(NULL, arraylist_search (arraylist, NULL, NULL, NULL));
+ POINTERS_EQUAL(NULL,
+ arraylist_search (NULL, (void *)item_abc, NULL, NULL));
+ POINTERS_EQUAL(NULL,
+ arraylist_search (arraylist, (void *)item_abc, NULL, NULL));
+
+ /* invalid add of element */
+ LONGS_EQUAL(-1, arraylist_add (NULL, NULL));
+
+ /* add some elements */
+ if (sorted)
+ {
+ TEST_ARRAYLIST_ADD(0, item_xxx);
+ TEST_ARRAYLIST_ADD(0, NULL);
+ TEST_ARRAYLIST_ADD(1, item_def);
+ TEST_ARRAYLIST_ADD(1, item_DEF);
+ TEST_ARRAYLIST_ADD(1, item_abc);
+ }
+ else
+ {
+ TEST_ARRAYLIST_ADD(0, item_xxx);
+ TEST_ARRAYLIST_ADD(1, NULL);
+ TEST_ARRAYLIST_ADD(2, item_def);
+ TEST_ARRAYLIST_ADD((allow_duplicates) ? 3 : 2, item_DEF);
+ TEST_ARRAYLIST_ADD((allow_duplicates) ? 4 : 3, item_abc);
+ }
+
+ /*
+ * arraylist is now:
+ * sorted:
+ * allow dup: [NULL, "abc", "DEF", "def", "xxx", (NULL)]
+ * no dup : [NULL, "abc", "DEF", "xxx"]
+ * not sorted:
+ * allow dup: ["xxx", NULL, "def", "DEF", "abc", (NULL)]
+ * no dup : ["xxx", NULL, "DEF", "abc"]
+ */
+
+ /* check size after adds */
+ LONGS_EQUAL((allow_duplicates) ? 5 : 4, arraylist->size);
+ LONGS_EQUAL((allow_duplicates) ? 5 : 4, arraylist_size (arraylist));
+ LONGS_EQUAL((allow_duplicates) ? 6 : 4, arraylist->size_alloc);
+
+ /* check content after adds */
+ if (sorted)
+ {
+ POINTERS_EQUAL(NULL, arraylist->data[0]);
+ STRCMP_EQUAL(item_abc, (const char *)arraylist->data[1]);
+ if (allow_duplicates)
+ {
+ STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[2]);
+ STRCMP_EQUAL(item_def, (const char *)arraylist->data[3]);
+ STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[4]);
+ POINTERS_EQUAL(NULL, arraylist->data[5]);
+ }
+ else
+ {
+ STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[2]);
+ STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[3]);
+ }
+ }
+ else
+ {
+ STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[0]);
+ POINTERS_EQUAL(NULL, arraylist->data[1]);
+ if (allow_duplicates)
+ {
+ STRCMP_EQUAL(item_def, (const char *)arraylist->data[2]);
+ STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[3]);
+ STRCMP_EQUAL(item_abc, (const char *)arraylist->data[4]);
+ POINTERS_EQUAL(NULL, arraylist->data[5]);
+ }
+ else
+ {
+ STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[2]);
+ STRCMP_EQUAL(item_abc, (const char *)arraylist->data[3]);
+ }
+ }
+
+ /* search elements */
+ if (sorted)
+ {
+ /* search first element */
+ POINTERS_EQUAL(NULL, arraylist_search (arraylist, NULL,
+ &index, &index_insert));
+ LONGS_EQUAL(0, index);
+ LONGS_EQUAL(0, index_insert);
+
+ /* search second element */
+ POINTERS_EQUAL(item_abc, arraylist_search (arraylist, (void *)item_abc,
+ &index, &index_insert));
+ LONGS_EQUAL(1, index);
+ LONGS_EQUAL(1, index_insert);
+
+ /* search last element */
+ POINTERS_EQUAL(item_xxx,
+ arraylist_search (arraylist, (void *)item_xxx,
+ &index, &index_insert));
+ LONGS_EQUAL((allow_duplicates) ? 4 : 3, index);
+ LONGS_EQUAL(-1, index_insert);
+
+ /* search non-existing element */
+ POINTERS_EQUAL(NULL,
+ arraylist_search (arraylist, (void *)item_aaa,
+ &index, &index_insert));
+ LONGS_EQUAL(-1, index);
+ LONGS_EQUAL(1, index_insert);
+ }
+ else
+ {
+ /* search first element */
+ POINTERS_EQUAL(item_xxx, arraylist_search (arraylist, (void *)item_xxx,
+ &index, &index_insert));
+ LONGS_EQUAL(0, index);
+ LONGS_EQUAL(-1, index_insert);
+
+ /* search second element */
+ POINTERS_EQUAL(NULL, arraylist_search (arraylist, NULL,
+ &index, &index_insert));
+ LONGS_EQUAL(1, index);
+ LONGS_EQUAL(-1, index_insert);
+
+ /* search last element */
+ POINTERS_EQUAL(item_abc,
+ arraylist_search (arraylist, (void *)item_abc,
+ &index, &index_insert));
+ LONGS_EQUAL((allow_duplicates) ? 4 : 3, index);
+ LONGS_EQUAL(-1, index_insert);
+
+ /* search non-existing element */
+ POINTERS_EQUAL(NULL,
+ arraylist_search (arraylist, (void *)item_aaa,
+ &index, &index_insert));
+ LONGS_EQUAL(-1, index);
+ LONGS_EQUAL(-1, index_insert);
+ }
+
+ /* invalid remove of elements */
+ LONGS_EQUAL(-1, arraylist_remove (NULL, -1));
+ LONGS_EQUAL(-1, arraylist_remove (arraylist, -1));
+ LONGS_EQUAL(-1, arraylist_remove (NULL, 0));
+
+ /* remove the 3 first elements and check size after each remove */
+ LONGS_EQUAL(0, arraylist_remove (arraylist, 0));
+ LONGS_EQUAL((allow_duplicates) ? 4 : 3, arraylist->size);
+ LONGS_EQUAL((allow_duplicates) ? 4 : 3, arraylist_size (arraylist));
+ LONGS_EQUAL((allow_duplicates) ? 6 : 4, arraylist->size_alloc);
+ LONGS_EQUAL(0, arraylist_remove (arraylist, 0));
+ LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist->size);
+ LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist_size (arraylist));
+ LONGS_EQUAL((allow_duplicates) ? 6 : 4, arraylist->size_alloc);
+ LONGS_EQUAL(0, arraylist_remove (arraylist, 0));
+ LONGS_EQUAL((allow_duplicates) ? 2 : 1, arraylist->size);
+ LONGS_EQUAL((allow_duplicates) ? 2 : 1, arraylist_size (arraylist));
+ LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist->size_alloc);
+
+ /*
+ * arraylist is now:
+ * sorted:
+ * allow dup: ["def", "xxx", (NULL)]
+ * no dup : ["xxx"]
+ * not sorted:
+ * allow dup: ["DEF", "abc", (NULL)]
+ * no dup : ["abc"]
+ */
+
+ /* check content after the 3 deletions */
+ if (sorted)
+ {
+ if (allow_duplicates)
+ {
+ STRCMP_EQUAL(item_def, (const char *)arraylist->data[0]);
+ STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[1]);
+ POINTERS_EQUAL(NULL, arraylist->data[2]);
+ }
+ else
+ {
+ STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[0]);
+ }
+ }
+ else
+ {
+ if (allow_duplicates)
+ {
+ STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[0]);
+ STRCMP_EQUAL(item_abc, (const char *)arraylist->data[1]);
+ POINTERS_EQUAL(NULL, arraylist->data[2]);
+ }
+ else
+ {
+ STRCMP_EQUAL(item_abc, (const char *)arraylist->data[0]);
+ }
+ }
+
+ /* invalid insert of element */
+ LONGS_EQUAL(-1, arraylist_insert (NULL, 0, NULL));
+
+ /* insert of one element */
+ LONGS_EQUAL(0, arraylist_insert (arraylist, 0, (void *)item_aaa));
+
+ /*
+ * arraylist is now:
+ * sorted:
+ * allow dup: ["aaa", "def", "xxx", (NULL)]
+ * no dup : ["aaa", "xxx"]
+ * not sorted:
+ * allow dup: ["aaa", "DEF", "abc", (NULL)]
+ * no dup : ["aaa", "abc"]
+ */
+
+ /* check size after insert */
+ LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist->size);
+ LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist_size (arraylist));
+ LONGS_EQUAL((allow_duplicates) ? 3 : 2, arraylist->size_alloc);
+
+ /* check content after the insert */
+ if (sorted)
+ {
+ if (allow_duplicates)
+ {
+ STRCMP_EQUAL(item_aaa, (const char *)arraylist->data[0]);
+ STRCMP_EQUAL(item_def, (const char *)arraylist->data[1]);
+ STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[2]);
+ }
+ else
+ {
+ STRCMP_EQUAL(item_aaa, (const char *)arraylist->data[0]);
+ STRCMP_EQUAL(item_xxx, (const char *)arraylist->data[1]);
+ }
+ }
+ else
+ {
+ if (allow_duplicates)
+ {
+ STRCMP_EQUAL(item_aaa, (const char *)arraylist->data[0]);
+ STRCMP_EQUAL(item_DEF, (const char *)arraylist->data[1]);
+ STRCMP_EQUAL(item_abc, (const char *)arraylist->data[2]);
+ }
+ else
+ {
+ STRCMP_EQUAL(item_aaa, (const char *)arraylist->data[0]);
+ STRCMP_EQUAL(item_abc, (const char *)arraylist->data[1]);
+ }
+ }
+
+ /* clear arraylist */
+ LONGS_EQUAL(0, arraylist_clear (NULL));
+ LONGS_EQUAL(1, arraylist_clear (arraylist));
+
+ /* check size and data after clear */
+ LONGS_EQUAL(0, arraylist->size);
+ LONGS_EQUAL(0, arraylist_size (arraylist));
+ LONGS_EQUAL(initial_size, arraylist->size_alloc);
+ if (initial_size > 0)
+ {
+ CHECK(arraylist->data);
+ for (i = 0; i < initial_size; i++)
+ {
+ POINTERS_EQUAL(NULL, arraylist->data[i]);
+ }
+ }
+ else
+ {
+ POINTERS_EQUAL(NULL, arraylist->data);
+ }
+
+ /* free arraylist */
+ arraylist_free (arraylist);
+}
+
+/*
+ * Tests functions:
+ * arraylist_new
+ * arraylist_size
+ * arraylist_get
+ * arraylist_search
+ * arraylist_insert
+ * arraylist_add
+ * arraylist_remove
+ * arraylist_clear
+ * arraylist_free
+ */
+
+TEST(Arraylist, New)
+{
+ int initial_size, sorted, allow_duplicates;
+
+ /*
+ * in order to create an arraylist, initial_size must be >= 0 and a
+ * comparison callback must be given
+ */
+ POINTERS_EQUAL(NULL,
+ arraylist_new (-1, 0, 0, NULL, NULL, NULL, NULL));
+ POINTERS_EQUAL(NULL,
+ arraylist_new (-1, 0, 0, &test_cmp_cb, NULL, NULL, NULL));
+ POINTERS_EQUAL(NULL,
+ arraylist_new (0, 0, 0, NULL, NULL, NULL, NULL));
+
+ /* tests on arraylists */
+ for (initial_size = 0; initial_size < 2; initial_size++)
+ {
+ for (sorted = 0; sorted < 2; sorted++)
+ {
+ for (allow_duplicates = 0; allow_duplicates < 2;
+ allow_duplicates++)
+ {
+ test_arraylist (initial_size, sorted, allow_duplicates);
+ }
+ }
+ }
+}