[anjuta] libanjuta: Add new AnjutaCompletion object for prefix matching
- From: Carl-Anton Ingmarsson <carlantoni src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [anjuta] libanjuta: Add new AnjutaCompletion object for prefix matching
- Date: Mon, 18 Feb 2013 19:21:41 +0000 (UTC)
commit 53521666e020b2be05c0ff7a0964f1f6cfd98329
Author: Carl-Anton Ingmarsson <ca ingmarsson gmail com>
Date: Fri Feb 1 17:37:35 2013 +0100
libanjuta: Add new AnjutaCompletion object for prefix matching
AnjutaCompletion is a replacement for the deprecated GCompletion.
It should potentially be faster than GCompletion since it uses two
binary searches to find the matching completions instead of doing
a linear search which GCompletion does.
https://bugzilla.gnome.org/show_bug.cgi?id=694048
libanjuta/Makefile.am | 4 +-
libanjuta/anjuta-completion.c | 399 +++++++++++++++++++++++++++++++++++++++++
libanjuta/anjuta-completion.h | 93 ++++++++++
3 files changed, 495 insertions(+), 1 deletions(-)
---
diff --git a/libanjuta/Makefile.am b/libanjuta/Makefile.am
index 4801567..327008d 100644
--- a/libanjuta/Makefile.am
+++ b/libanjuta/Makefile.am
@@ -132,7 +132,9 @@ libanjuta_3_la_SOURCES= \
anjuta-tree-combo.h \
anjuta-tree-combo.c \
anjuta-autogen.h \
- anjuta-autogen.c
+ anjuta-autogen.c \
+ anjuta-completion.h \
+ anjuta-completion.c
# Glade module
if HAVE_PLUGIN_GLADE
diff --git a/libanjuta/anjuta-completion.c b/libanjuta/anjuta-completion.c
new file mode 100644
index 0000000..dee0d40
--- /dev/null
+++ b/libanjuta/anjuta-completion.c
@@ -0,0 +1,399 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * anjuta-completion.c
+ * Copyright (C) 2013 Carl-Anton Ingmarsson <carlantoni gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * anjuta 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+#include <string.h>
+
+#include "anjuta-completion.h"
+
+
+struct _AnjutaCompletionPrivate
+{
+ GPtrArray* items;
+ gboolean items_sorted;
+
+ char* last_complete;
+ gint last_complete_start;
+ gint last_complete_end;
+
+ AnjutaCompletionNameFunc name_func;
+ GDestroyNotify item_destroy_func;
+
+ AnjutaCompletionFilterFunc filter_func;
+ void* filter_func_user_data;
+
+ /* Properties */
+ gboolean case_sensitive;
+};
+
+enum
+{
+ PROP_0,
+ PROP_CASE_SENSITIVE,
+ PROP_LAST
+};
+static GParamSpec* properties[PROP_LAST];
+
+G_DEFINE_TYPE (AnjutaCompletion, anjuta_completion, G_TYPE_OBJECT);
+
+static inline
+const char* anjuta_completion_default_name_func (const void* item)
+{
+ return (const char*)item;
+}
+
+static gint
+anjuta_completion_item_sort_func (gconstpointer a, gconstpointer b,
+ void* user_data)
+{
+ AnjutaCompletion* self = ANJUTA_COMPLETION (user_data);
+
+ const char* name_a;
+ const char* name_b;
+
+ name_a = self->priv->name_func (*(const void**)a);
+ name_b = self->priv->name_func (*(const void**)b);
+
+ if (self->priv->case_sensitive)
+ return strcmp (name_a, name_b);
+ else
+ return g_ascii_strcasecmp (name_a, name_b);
+}
+
+/**
+ * anjuta_completion_complete:
+ * @self: A #AnjutaCompletion
+ * @prefix: The prefix to complete against.
+ *
+ * Returns: (transfer container): The list of completions that matched
+ * @prefix.
+ */
+GList*
+anjuta_completion_complete (AnjutaCompletion* self,
+ const char* prefix,
+ gint max_completions)
+
+{
+ gint start, end;
+ gint l, r;
+ gint (*ncmp_func)(const char* s1, const char* s2, gsize n);
+ gint first_match, last_match;
+ GList* completions = NULL;
+
+ g_return_val_if_fail (ANJUTA_IS_COMPLETION (self), NULL);
+ g_return_val_if_fail (prefix, NULL);
+
+ /* Start from old completion if possible */
+ if (self->priv->last_complete &&
+ self->priv->items_sorted &&
+ g_str_has_prefix (prefix, self->priv->last_complete))
+ {
+ start = self->priv->last_complete_start;
+ end = self->priv->last_complete_end;
+ }
+ else
+ {
+ start = 0;
+ end = self->priv->items->len - 1;
+ }
+
+ /* Free last complete */
+ if (self->priv->last_complete)
+ {
+ g_free (self->priv->last_complete);
+ self->priv->last_complete = NULL;
+ }
+
+ /* Sort the items if they're not already sorted */
+ if (!self->priv->items_sorted)
+ {
+ g_ptr_array_sort_with_data (self->priv->items, anjuta_completion_item_sort_func,
+ self);
+ self->priv->items_sorted = TRUE;
+ }
+
+ ncmp_func = self->priv->case_sensitive ? strncmp : g_ascii_strncasecmp;
+
+ /* Do a binary search to find the first match */
+ for (l = start, r = end; l < r;)
+ {
+ gint mid;
+ void* item;
+ const char* name;
+ gint cmp;
+
+ mid = l + (r - l) / 2;
+ item = g_ptr_array_index (self->priv->items, mid);
+ name = self->priv->name_func (item);
+
+ cmp = ncmp_func (prefix, name, strlen (prefix));
+ if (cmp > 0)
+ l = mid + 1;
+ else if (cmp < 0)
+ r = mid - 1;
+ else
+ r = mid;
+ }
+ first_match = l;
+
+ /* Do a binary search to find the last match */
+ for (l = first_match, r = end; l < r;)
+ {
+ gint mid;
+ void* item;
+ const char* name;
+ gint cmp;
+
+ mid = l + ((r - l) / 2) + 1;
+ item = g_ptr_array_index (self->priv->items, mid);
+ name = self->priv->name_func (item);
+
+ cmp = ncmp_func (prefix, name, strlen (prefix));
+ if (cmp == 0)
+ l = mid;
+ else
+ r = mid - 1;
+
+ }
+ last_match = r;
+
+ if (first_match <= last_match)
+ {
+ gint i;
+ gint n_completions;
+
+ n_completions = 0;
+ for (i = first_match; i <= last_match; i++)
+ {
+ void* item;
+ const char* name;
+
+ item = g_ptr_array_index (self->priv->items, i);
+ name = self->priv->name_func (item);
+
+ if (ncmp_func (prefix, name, strlen (prefix)) != 0)
+ break;
+
+ if (self->priv->filter_func &&
+ !self->priv->filter_func (item, self->priv->filter_func_user_data))
+ continue;
+
+ completions = g_list_prepend (completions, item);
+ n_completions++;
+ if (max_completions > 0 && n_completions == max_completions)
+ break;
+ }
+ completions = g_list_reverse (completions);
+ }
+
+ self->priv->last_complete = g_strdup (prefix);
+ self->priv->last_complete_start = first_match;
+ self->priv->last_complete_end = last_match;
+
+ return completions;
+}
+
+static void
+anjuta_completion_clear_items (AnjutaCompletion* self)
+{
+ if (self->priv->item_destroy_func)
+ {
+ gint i;
+
+ for (i = 0; i < self->priv->items->len; i++)
+ {
+ void* item = g_ptr_array_index (self->priv->items, i);
+
+ self->priv->item_destroy_func (item);
+ }
+ }
+ g_ptr_array_unref (self->priv->items);
+
+ g_free (self->priv->last_complete);
+ self->priv->last_complete = NULL;
+}
+
+/**
+ * anjuta_completion_clear:
+ * @self: a #AnjutaCompletion
+ *
+ * Clear all items added to the completion.
+ */
+void
+anjuta_completion_clear (AnjutaCompletion* self)
+{
+ g_return_if_fail (ANJUTA_IS_COMPLETION (self));
+
+ anjuta_completion_clear_items (self);
+ self->priv->items = g_ptr_array_new ();
+}
+
+/**
+ * anjuta_completion_add_item:
+ * @self: a #AnjutaCompletion
+ * @item: the item to be added.
+ *
+ * Add an item to the completion.
+ */
+void
+anjuta_completion_add_item (AnjutaCompletion* self,
+ void* item)
+{
+ g_ptr_array_add (self->priv->items, item);
+
+ self->priv->items_sorted = FALSE;
+}
+
+void
+anjuta_completion_set_filter_func (AnjutaCompletion* self,
+ AnjutaCompletionFilterFunc filter_func,
+ void* user_data)
+{
+ g_return_if_fail (ANJUTA_IS_COMPLETION (self));
+
+ self->priv->filter_func = filter_func;
+ self->priv->filter_func_user_data = user_data;
+}
+
+/**
+ * anjuta_completion_set_item_destroy_func:
+ * @self: a #AnjutaCompletion
+ * @item_destroy_func: (allow none): the function to be called on
+ * the added items when the #AnjutaCompletion object is destroyed.
+ */
+void
+anjuta_completion_set_item_destroy_func (AnjutaCompletion* self,
+ GDestroyNotify item_destroy_func)
+{
+ g_return_if_fail (ANJUTA_IS_COMPLETION (self));
+
+ self->priv->item_destroy_func = item_destroy_func;
+}
+
+gboolean
+anjuta_completion_get_case_sensitive (AnjutaCompletion* self)
+{
+ g_return_val_if_fail (ANJUTA_IS_COMPLETION (self), FALSE);
+
+ return self->priv->case_sensitive;
+}
+
+void
+anjuta_completion_set_case_sensitive (AnjutaCompletion* self,
+ gboolean case_sensitive)
+{
+ g_return_if_fail (ANJUTA_IS_COMPLETION (self));
+
+ if (self->priv->case_sensitive == case_sensitive)
+ return;
+
+ g_free (self->priv->last_complete);
+ self->priv->last_complete = NULL;
+
+ self->priv->items_sorted = FALSE;
+
+ self->priv->case_sensitive = case_sensitive;
+ g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CASE_SENSITIVE]);
+}
+
+AnjutaCompletion*
+anjuta_completion_new (AnjutaCompletionNameFunc name_func)
+{
+ AnjutaCompletion* self;
+
+ self = g_object_new (ANJUTA_TYPE_COMPLETION, NULL);
+
+ if (name_func)
+ self->priv->name_func = name_func;
+
+ return self;
+}
+
+static void
+anjuta_completion_init (AnjutaCompletion* self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ANJUTA_TYPE_COMPLETION,
+ AnjutaCompletionPrivate);
+
+ self->priv->name_func = anjuta_completion_default_name_func;
+ self->priv->case_sensitive = TRUE;
+
+ self->priv->items = g_ptr_array_new ();
+}
+
+static void
+anjuta_completion_finalize (GObject *object)
+{
+ AnjutaCompletion* self = ANJUTA_COMPLETION (object);
+
+ anjuta_completion_clear_items (self);
+
+ G_OBJECT_CLASS (anjuta_completion_parent_class)->finalize (object);
+}
+
+static void
+anjuta_completion_get_property (GObject* object, guint prop_id, GValue* value,
+ GParamSpec* pspec)
+{
+ AnjutaCompletion* self = ANJUTA_COMPLETION (object);
+
+ switch (prop_id)
+ {
+ case PROP_CASE_SENSITIVE:
+ g_value_set_boolean (value,
+ anjuta_completion_get_case_sensitive (self));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+anjuta_completion_set_property (GObject* object, guint prop_id,
+ const GValue* value, GParamSpec* pspec)
+{
+ AnjutaCompletion* self = ANJUTA_COMPLETION (object);
+
+ switch (prop_id)
+ {
+ case PROP_CASE_SENSITIVE:
+ anjuta_completion_set_case_sensitive (self, g_value_get_boolean (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+anjuta_completion_class_init (AnjutaCompletionClass *klass)
+{
+ GObjectClass* object_class = G_OBJECT_CLASS (klass);
+
+ object_class->get_property = anjuta_completion_get_property;
+ object_class->set_property = anjuta_completion_set_property;
+ object_class->finalize = anjuta_completion_finalize;
+
+ properties[PROP_CASE_SENSITIVE] =
+ g_param_spec_boolean ("case-sensitive", "Case sensitive", "Case sensitive",
+ TRUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties (object_class, PROP_LAST, properties);
+ g_type_class_add_private (klass, sizeof (AnjutaCompletionPrivate));
+}
diff --git a/libanjuta/anjuta-completion.h b/libanjuta/anjuta-completion.h
new file mode 100644
index 0000000..bc2b441
--- /dev/null
+++ b/libanjuta/anjuta-completion.h
@@ -0,0 +1,93 @@
+/* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4; tab-width: 4 -*- */
+/*
+ * anjuta-completion.h
+ * Copyright (C) 2013 Carl-Anton Ingmarsson <carlantoni gnome org>
+ *
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * anjuta 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.";
+ */
+
+#ifndef _ANJUTA_COMPLETION_H_
+#define _ANJUTA_COMPLETION_H_
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define ANJUTA_TYPE_COMPLETION (anjuta_completion_get_type ())
+#define ANJUTA_COMPLETION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), ANJUTA_TYPE_COMPLETION,
AnjutaCompletion))
+#define ANJUTA_COMPLETION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), ANJUTA_TYPE_COMPLETION,
AnjutaCompletionClass))
+#define ANJUTA_IS_COMPLETION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), ANJUTA_TYPE_COMPLETION))
+#define ANJUTA_IS_COMPLETION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), ANJUTA_TYPE_COMPLETION))
+#define ANJUTA_COMPLETION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), ANJUTA_TYPE_COMPLETION,
AnjutaCompletionClass))
+
+typedef struct _AnjutaCompletionClass AnjutaCompletionClass;
+typedef struct _AnjutaCompletion AnjutaCompletion;
+typedef struct _AnjutaCompletionPrivate AnjutaCompletionPrivate;
+
+
+
+struct _AnjutaCompletionClass
+{
+ GObjectClass parent_class;
+};
+
+struct _AnjutaCompletion
+{
+ GObject parent_instance;
+
+ AnjutaCompletionPrivate* priv;
+};
+
+
+typedef const char* (*AnjutaCompletionNameFunc) (const void* item);
+typedef gboolean (*AnjutaCompletionFilterFunc) (const void* item, void* user_data);
+
+
+GType anjuta_completion_get_type (void) G_GNUC_CONST;
+
+AnjutaCompletion*
+anjuta_completion_new (AnjutaCompletionNameFunc name_func);
+
+gboolean
+anjuta_completion_get_case_sensitive (AnjutaCompletion* self);
+
+void
+anjuta_completion_set_case_sensitive (AnjutaCompletion* self,
+ gboolean case_sensitive);
+
+void
+anjuta_completion_set_item_destroy_func (AnjutaCompletion* self,
+ GDestroyNotify item_destroy_func);
+
+void
+anjuta_completion_set_filter_func (AnjutaCompletion* self,
+ AnjutaCompletionFilterFunc filter_func,
+ void* user_data);
+
+void
+anjuta_completion_add_item (AnjutaCompletion* self,
+ void* item);
+
+void
+anjuta_completion_clear (AnjutaCompletion* self);
+
+GList*
+anjuta_completion_complete (AnjutaCompletion* self,
+ const char* prefix,
+ gint max_completions);
+
+G_END_DECLS
+
+#endif /* _ANJUTA_COMPLETION_H_ */
+
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]