[gnome-builder] libide: add IdeCompletionItem and IdeCompletionResults helpers



commit 244dd89121e6cc642c23a8c5db39c7ec9a50964d
Author: Christian Hergert <christian hergert me>
Date:   Mon Sep 28 06:18:31 2015 -0700

    libide: add IdeCompletionItem and IdeCompletionResults helpers
    
    These abstractions make it easier to write fast autocompletion providers
    for GtkSourceView. They abstract the GList embedded link node hack as well
    as dealing with checking if a query can be replayed (and performing that
    as well).
    
    Consumers should override IdeCompletionItem::match() and
    IdeCompletionResults::compare() (or set the items priority appropriately).

 libide/Makefile.am              |    4 +
 libide/ide-completion-item.c    |  139 +++++++++++++
 libide/ide-completion-item.h    |  108 ++++++++++
 libide/ide-completion-results.c |  413 +++++++++++++++++++++++++++++++++++++++
 libide/ide-completion-results.h |   64 ++++++
 5 files changed, 728 insertions(+), 0 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 879fb2f..918cc0c 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -33,8 +33,12 @@ libide_1_0_la_public_sources = \
        ide-build-system.h \
        ide-builder.c \
        ide-builder.h \
+       ide-completion-item.c \
+       ide-completion-item.h \
        ide-completion-provider.c \
        ide-completion-provider.h \
+       ide-completion-results.c \
+       ide-completion-results.h \
        ide-context.c \
        ide-context.h \
        ide-debugger.c \
diff --git a/libide/ide-completion-item.c b/libide/ide-completion-item.c
new file mode 100644
index 0000000..b350cf4
--- /dev/null
+++ b/libide/ide-completion-item.c
@@ -0,0 +1,139 @@
+/* ide-completion-item.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-completion-item"
+
+#include <string.h>
+
+#include "ide-completion-item.h"
+
+G_DEFINE_ABSTRACT_TYPE_WITH_CODE (IdeCompletionItem, ide_completion_item, G_TYPE_OBJECT,
+                                  G_IMPLEMENT_INTERFACE (GTK_SOURCE_TYPE_COMPLETION_PROPOSAL, NULL))
+
+static gboolean
+ide_completion_item_real_match (IdeCompletionItem *self,
+                                const gchar       *query,
+                                const gchar       *casefold)
+{
+  gchar *text;
+  gboolean ret;
+
+  g_assert (IDE_IS_COMPLETION_ITEM (self));
+  g_assert (query != NULL);
+  g_assert (casefold != NULL);
+
+  text = ide_completion_item_get_column_markup (self, IDE_COMPLETION_COLUMN_PRIMARY);
+  ret = !!strstr (text ?: "", query);
+  g_free (text);
+
+  return ret;
+}
+
+static gchar *
+ide_completion_item_real_get_column_markup (IdeCompletionItem   *self,
+                                            IdeCompletionColumn  column)
+{
+  g_return_val_if_fail (IDE_IS_COMPLETION_ITEM (self), NULL);
+
+  if (column == IDE_COMPLETION_COLUMN_PRIMARY)
+    return gtk_source_completion_proposal_get_markup (GTK_SOURCE_COMPLETION_PROPOSAL (self));
+
+  return NULL;
+}
+
+gboolean
+ide_completion_item_match (IdeCompletionItem *self,
+                           const gchar       *query,
+                           const gchar       *casefold)
+{
+  g_return_val_if_fail (IDE_IS_COMPLETION_ITEM (self), FALSE);
+
+  return IDE_COMPLETION_ITEM_GET_CLASS (self)->match (self, query, casefold);
+}
+
+gchar *
+ide_completion_item_get_column_markup (IdeCompletionItem   *self,
+                                       IdeCompletionColumn  column)
+{
+  g_return_val_if_fail (IDE_IS_COMPLETION_ITEM (self), FALSE);
+
+  return IDE_COMPLETION_ITEM_GET_CLASS (self)->get_column_markup (self, column);
+}
+
+static void
+ide_completion_item_class_init (IdeCompletionItemClass *klass)
+{
+  klass->get_column_markup = ide_completion_item_real_get_column_markup;
+  klass->match = ide_completion_item_real_match;
+}
+
+static void
+ide_completion_item_init (IdeCompletionItem *self)
+{
+  self->parent_instance.link.data = self;
+}
+
+/**
+ * ide_completion_item_fuzzy_match:
+ * @haystack: the string to be searched.
+ * @casefold_needle: A g_utf8_casefold() version of the needle.
+ * @priority: (out) (allow-none): An optional location for the score of the match
+ *
+ * This helper function can do a fuzzy match for you giving a haystack and
+ * casefolded needle. Casefold your needle using g_utf8_casefold() before
+ * running the query against a batch of #IdeCompletionItem for the best performance.
+ *
+ * score will be set with the score of the match upon success. Otherwise,
+ * it will be set to zero.
+ *
+ * Returns: %TRUE if @haystack matched @casefold_needle, otherwise %FALSE.
+ */
+gboolean
+ide_completion_item_fuzzy_match (const gchar *haystack,
+                                 const gchar *casefold_needle,
+                                 guint       *priority)
+{
+  gint real_score = 0;
+
+  for (; *casefold_needle; casefold_needle = g_utf8_next_char (casefold_needle))
+    {
+      gunichar ch = g_utf8_get_char (casefold_needle);
+      const gchar *tmp;
+
+      /*
+       * Note that the following code is not really correct. We want
+       * to be relatively fast here, but we also don't want to convert
+       * strings to casefolded versions for querying on each compare.
+       * So we use the casefold version and compare with upper. This
+       * works relatively well since we are usually dealing with ASCII
+       * for function names and symbols.
+       */
+      tmp = strchr (haystack, ch);
+      if (tmp == NULL)
+        tmp = strchr (haystack, g_unichar_toupper (ch));
+      if (tmp == NULL)
+        return FALSE;
+      real_score += (tmp - haystack);
+      haystack = tmp;
+    }
+
+  if (priority != NULL)
+    *priority = real_score + strlen (haystack);
+
+  return TRUE;
+}
diff --git a/libide/ide-completion-item.h b/libide/ide-completion-item.h
new file mode 100644
index 0000000..444f8d7
--- /dev/null
+++ b/libide/ide-completion-item.h
@@ -0,0 +1,108 @@
+/* ide-completion-item.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_COMPLETION_ITEM_H
+#define IDE_COMPLETION_ITEM_H
+
+#include <gtksourceview/gtksource.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_COMPLETION_ITEM (ide_completion_item_get_type())
+
+/*
+ * We provide this private, but defined in the header API for IdeCompletionItemHead
+ * so that we have a location to store a GList node without allocating one. This is
+ * used for sorting of result structures by IdeCompletionResults without allocating
+ * GList items for GtkSourceCompletionContext.
+ */
+typedef struct
+{
+  GObject parent;
+  /*< semi-public >*/
+  GList link;
+  guint priority;
+} IdeCompletionItemHead;
+
+/*
+ * We require the following cleanup function so that G_DECLARE_DERIVABLE_TYPE() can
+ * work with our semi-public IdeCompletionItemHead node.
+ */
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (IdeCompletionItemHead, g_object_unref)
+
+G_DECLARE_DERIVABLE_TYPE (IdeCompletionItem,
+                          ide_completion_item,
+                          IDE, COMPLETION_ITEM,
+                          IdeCompletionItemHead)
+
+typedef enum
+{
+  IDE_COMPLETION_COLUMN_PRIMARY = 0,
+  IDE_COMPLETION_COLUMN_PREFIX  = 1,
+  IDE_COMPLETION_COLUMN_SUFFIX  = 2,
+  IDE_COMPLETION_COLUMN_INFO    = 3,
+} IdeCompletionColumn;
+
+struct _IdeCompletionItemClass
+{
+  GObjectClass parent;
+
+  /**
+   * IdeCompletionItem::match:
+   *
+   * This virtual function checks to see if a particular query matches
+   * the #IdeCompletionItem in question. You can use helper functions
+   * defined in this module for simple requests like case-insensitive
+   * fuzzy matching.
+   *
+   * The default implementation of this virtual function performs a
+   * strstr() to match @query exactly in the items label.
+   *
+   * Returns: %TRUE if the item matches.
+   */
+  gboolean (*match) (IdeCompletionItem *self,
+                     const gchar       *query,
+                     const gchar       *casefold);
+
+  /**
+   * IdeCompletionItem::get_column_markup:
+   * @self: An #IdeCompletionItem.
+   * @column: The #IdeCompletionItemColumn to retrieve.
+   *
+   * This function returns the text for a particular column.
+   * This allows for Builder to organize results with aligned
+   * columns in GtkSourceView. NOTE: This is not yet performed
+   * today, but will in the future.
+   */
+  gchar *(*get_column_markup) (IdeCompletionItem   *self,
+                               IdeCompletionColumn  column);
+};
+
+IdeCompletionItem *ide_completion_item_new               (void);
+gboolean           ide_completion_item_match             (IdeCompletionItem   *self,
+                                                          const gchar         *query,
+                                                          const gchar         *casefold);
+gchar             *ide_completion_item_get_column_markup (IdeCompletionItem   *self,
+                                                          IdeCompletionColumn  column);
+gboolean           ide_completion_item_fuzzy_match       (const gchar         *haystack,
+                                                          const gchar         *casefold_needle,
+                                                          guint               *priority);
+
+G_END_DECLS
+
+#endif /* IDE_COMPLETION_ITEM_H */
diff --git a/libide/ide-completion-results.c b/libide/ide-completion-results.c
new file mode 100644
index 0000000..4b4e60a
--- /dev/null
+++ b/libide/ide-completion-results.c
@@ -0,0 +1,413 @@
+/* ide-completion-results.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "ide-completion-results"
+
+#include <string.h>
+
+#include "egg-counter.h"
+
+#include "ide-completion-results.h"
+#include "ide-debug.h"
+
+typedef struct
+{
+  /*
+   * needs_refilter indicates that the result set must have
+   * the linked list rebuilt from the array. Doing so must
+   * have match() called on each item to determine its
+   * visibility.
+   */
+  guint needs_refilter : 1;
+  /*
+   * If a g_list_sort() needs to be called on our synthesized
+   * linked list of visible items.
+   */
+  guint needs_sort : 1;
+  /*
+   * If can_reuse_list is set, refilter requests may traverse
+   * the linked list instead of a full array scan.
+   */
+  guint can_reuse_list : 1;
+  /*
+   * results contains all of our IdeCompletionItem results.
+   * We use this array of items with embedded GList links to
+   * create a zero-allocation linked list (well technically
+   * the item is an allocation, but we can't get around that).
+   */
+  GPtrArray *results;
+  /*
+   * query is the filtering string that was used to create the
+   * initial set of results. All future queries must have this
+   * word as a prefix to be reusable.
+   */
+  gchar *query;
+  /*
+   * replay is the word that was replayed on the last call to
+   * ide_completion_results_replay(). It allows us to continually
+   * dive down in the result set without looking at all items.
+   */
+  gchar *replay;
+  /*
+   * As an optimization, the linked list for result nodes are
+   * embedded in the IdeCompletionItem structures and we do not
+   * allocate them. This is the pointer to the first item in the
+   * result set that matches our query. It is not allocated
+   * and do not tree to free it or perform g_list_*() operations
+   * upon it except for g_list_sort().
+   */
+  GList *head;
+} IdeCompletionResultsPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeCompletionResults, ide_completion_results, G_TYPE_OBJECT)
+
+EGG_DEFINE_COUNTER (instances, "IdeCompletionResults", "Instances", "Number of IdeCompletionResults")
+
+#define GET_ITEM(i) ((IdeCompletionItem*)(g_ptr_array_index((priv)->results, (i))))
+#define GET_ITEM_LINK(item) (&((IdeCompletionItemHead *)(item))->link)
+
+enum {
+  PROP_0,
+  PROP_QUERY,
+  LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+IdeCompletionResults *
+ide_completion_results_new (const gchar *query)
+{
+  return g_object_new (IDE_TYPE_COMPLETION_RESULTS,
+                       "query", query,
+                       NULL);
+}
+
+void
+ide_completion_results_take_proposal (IdeCompletionResults *self,
+                                      IdeCompletionItem    *item)
+{
+  IdeCompletionResultsPrivate *priv = ide_completion_results_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_COMPLETION_RESULTS (self));
+  g_return_if_fail (IDE_IS_COMPLETION_ITEM (item));
+
+  g_ptr_array_add (priv->results, item);
+
+  priv->needs_refilter = TRUE;
+  priv->needs_sort = TRUE;
+  priv->can_reuse_list = FALSE;
+}
+
+static void
+ide_completion_results_finalize (GObject *object)
+{
+  IdeCompletionResults *self = (IdeCompletionResults *)object;
+  IdeCompletionResultsPrivate *priv = ide_completion_results_get_instance_private (self);
+
+  g_clear_pointer (&priv->query, g_free);
+  g_clear_pointer (&priv->replay, g_free);
+  g_clear_pointer (&priv->results, g_ptr_array_unref);
+  priv->head = NULL;
+
+  G_OBJECT_CLASS (ide_completion_results_parent_class)->finalize (object);
+
+  EGG_COUNTER_DEC (instances);
+}
+
+const gchar *
+ide_completion_results_get_query (IdeCompletionResults *self)
+{
+  IdeCompletionResultsPrivate *priv = ide_completion_results_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_COMPLETION_RESULTS (self), NULL);
+
+  return priv->query;
+}
+
+static void
+ide_completion_results_set_query (IdeCompletionResults *self,
+                                  const gchar          *query)
+{
+  IdeCompletionResultsPrivate *priv = ide_completion_results_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_COMPLETION_RESULTS (self));
+  g_return_if_fail (priv->query == NULL);
+
+  if (query == NULL)
+    query = "";
+
+  priv->query = g_strdup (query);
+  priv->replay = g_strdup (query);
+  priv->can_reuse_list = FALSE;
+  priv->needs_refilter = TRUE;
+  priv->needs_sort = TRUE;
+}
+
+gboolean
+ide_completion_results_replay (IdeCompletionResults *self,
+                               const gchar          *query)
+{
+  IdeCompletionResultsPrivate *priv = ide_completion_results_get_instance_private (self);
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (IDE_IS_COMPLETION_RESULTS (self), FALSE);
+  g_return_val_if_fail (priv->query != NULL, FALSE);
+  g_return_val_if_fail (query != NULL, FALSE);
+
+  if (g_str_has_prefix (query, priv->query))
+    {
+      priv->can_reuse_list = (priv->replay != NULL && g_str_has_prefix (query, priv->replay));
+      priv->needs_refilter = TRUE;
+      priv->needs_sort = TRUE;
+
+      g_free (priv->replay);
+      priv->replay = g_strdup (query);
+
+      IDE_RETURN (TRUE);
+    }
+
+  IDE_RETURN (FALSE);
+}
+
+static void
+ide_completion_results_update_links (IdeCompletionResults *self)
+{
+  IdeCompletionResultsPrivate *priv = ide_completion_results_get_instance_private (self);
+  IdeCompletionItem *item;
+  IdeCompletionItem *next;
+  IdeCompletionItem *prev;
+  guint i;
+
+  g_assert (IDE_IS_COMPLETION_RESULTS (self));
+  g_assert (priv->results != NULL);
+
+  if (G_UNLIKELY (priv->results->len == 0))
+    {
+      priv->head = NULL;
+      return;
+    }
+
+  /* Unrolling loops for the gentoo crowd */
+
+  item = GET_ITEM (0);
+  GET_ITEM_LINK (item)->prev = NULL;
+  GET_ITEM_LINK (item)->next = (priv->results->len == 1)
+                             ? NULL
+                             : GET_ITEM_LINK (GET_ITEM (1));
+
+  priv->head = GET_ITEM_LINK (item);
+
+  prev = item;
+
+  for (i = 1; i < (priv->results->len - 1); i++)
+    {
+      item = GET_ITEM (i);
+      next = GET_ITEM (i + 1);
+
+      GET_ITEM_LINK (item)->prev = GET_ITEM_LINK (prev);
+      GET_ITEM_LINK (item)->next = GET_ITEM_LINK (next);
+
+      prev = item;
+    }
+
+  if (priv->results->len > 1)
+    {
+      item = GET_ITEM (priv->results->len - 1);
+      GET_ITEM_LINK (item)->prev = GET_ITEM_LINK (GET_ITEM (priv->results->len - 2));
+      GET_ITEM_LINK (item)->next = NULL;
+    }
+}
+
+static void
+ide_completion_results_refilter (IdeCompletionResults *self)
+{
+  IdeCompletionResultsPrivate *priv = ide_completion_results_get_instance_private (self);
+  g_autofree gchar *casefold = NULL;
+
+  g_assert (IDE_IS_COMPLETION_RESULTS (self));
+  g_assert (priv->results != NULL);
+
+  if (priv->query == NULL || priv->replay == NULL || priv->results->len == 0)
+    return;
+
+  /*
+   * By traversing the linked list nodes instead of the array, we allow
+   * ourselves to avoid rechecking items we already know filtered.
+   * We do need to be mindful of this in case the user backspaced
+   * and our list is no longer a continual "deep dive" of matched items.
+   */
+  if (G_UNLIKELY (!priv->can_reuse_list))
+    ide_completion_results_update_links (self);
+
+  casefold = g_utf8_casefold (priv->replay, -1);
+
+  if (G_UNLIKELY (!g_str_is_ascii (casefold)))
+    {
+      g_warning ("Item filtering requires ascii input.");
+      return;
+    }
+
+  for (GList *iter = priv->head; iter; iter = iter->next)
+    {
+      IdeCompletionItem *item = iter->data;
+
+      if (!IDE_COMPLETION_ITEM_GET_CLASS (item)->match (item, priv->replay, casefold))
+        {
+          if (iter->prev != NULL)
+            iter->prev->next = iter->next;
+          else
+            priv->head = iter->next;
+
+          if (iter->next != NULL)
+            iter->next->prev = iter->prev;
+        }
+    }
+}
+
+static gint
+ide_completion_results_sorter (gconstpointer a,
+                               gconstpointer b,
+                               gpointer      user_data)
+{
+  IdeCompletionResults *self = user_data;
+
+  return IDE_COMPLETION_RESULTS_GET_CLASS (self)->compare (self, (gpointer)a, (gpointer)b);
+}
+
+static void
+ide_completion_results_resort (IdeCompletionResults *self)
+{
+  IdeCompletionResultsPrivate *priv = ide_completion_results_get_instance_private (self);
+
+  g_assert (IDE_IS_COMPLETION_RESULTS (self));
+
+  priv->head = g_list_sort_with_data (priv->head, ide_completion_results_sorter, self);
+}
+
+void
+ide_completion_results_present (IdeCompletionResults        *self,
+                                GtkSourceCompletionProvider *provider,
+                                GtkSourceCompletionContext  *context)
+{
+  IdeCompletionResultsPrivate *priv = ide_completion_results_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_COMPLETION_RESULTS (self));
+  g_return_if_fail (GTK_SOURCE_IS_COMPLETION_PROVIDER (provider));
+  g_return_if_fail (GTK_SOURCE_IS_COMPLETION_CONTEXT (context));
+  g_return_if_fail (priv->query != NULL);
+  g_return_if_fail (priv->replay != NULL);
+
+  if (priv->needs_refilter)
+    {
+      ide_completion_results_refilter (self);
+      priv->needs_refilter = FALSE;
+    }
+
+  if (priv->needs_sort)
+    {
+      ide_completion_results_resort (self);
+      priv->needs_sort = FALSE;
+    }
+
+  gtk_source_completion_context_add_proposals (context, provider, priv->head, TRUE);
+}
+
+static gint
+ide_completion_results_compare (IdeCompletionResults *self,
+                                IdeCompletionItem    *left,
+                                IdeCompletionItem    *right)
+{
+  if (left->parent_instance.priority < right->parent_instance.priority)
+    return -1;
+  else if (left->parent_instance.priority > right->parent_instance.priority)
+    return 1;
+  else
+    return 0;
+}
+
+static void
+ide_completion_results_get_property (GObject    *object,
+                                     guint       prop_id,
+                                     GValue     *value,
+                                     GParamSpec *pspec)
+{
+  IdeCompletionResults *self = IDE_COMPLETION_RESULTS (object);
+
+  switch (prop_id)
+    {
+    case PROP_QUERY:
+      g_value_set_string (value, ide_completion_results_get_query (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_completion_results_set_property (GObject      *object,
+                                     guint         prop_id,
+                                     const GValue *value,
+                                     GParamSpec   *pspec)
+{
+  IdeCompletionResults *self = IDE_COMPLETION_RESULTS (object);
+
+  switch (prop_id)
+    {
+    case PROP_QUERY:
+      ide_completion_results_set_query (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_completion_results_class_init (IdeCompletionResultsClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_completion_results_finalize;
+  object_class->get_property = ide_completion_results_get_property;
+  object_class->set_property = ide_completion_results_set_property;
+
+  klass->compare = ide_completion_results_compare;
+
+  gParamSpecs [PROP_QUERY] =
+    g_param_spec_string ("query",
+                         "Query",
+                         "Query",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
+}
+
+static void
+ide_completion_results_init (IdeCompletionResults *self)
+{
+  IdeCompletionResultsPrivate *priv = ide_completion_results_get_instance_private (self);
+
+  EGG_COUNTER_INC (instances);
+
+  priv->results = g_ptr_array_new_with_free_func (g_object_unref);
+  priv->head = NULL;
+  priv->query = NULL;
+}
diff --git a/libide/ide-completion-results.h b/libide/ide-completion-results.h
new file mode 100644
index 0000000..7dfac94
--- /dev/null
+++ b/libide/ide-completion-results.h
@@ -0,0 +1,64 @@
+/* ide-completion-results.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_COMPLETION_RESULTS_H
+#define IDE_COMPLETION_RESULTS_H
+
+#include <gtksourceview/gtksource.h>
+
+#include "ide-completion-item.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_COMPLETION_RESULTS (ide_completion_results_get_type())
+
+G_DECLARE_DERIVABLE_TYPE (IdeCompletionResults, ide_completion_results, IDE, COMPLETION_RESULTS, GObject)
+
+struct _IdeCompletionResultsClass
+{
+  GObjectClass parent_class;
+
+  /**
+   * IdeCompletionResults::compare:
+   * @self: An #IdeCompletionResults
+   * @left: An #IdeCompletionItem on the left hand side.
+   * @right: An #IdeCompletionItem on the right hand side.
+   *
+   * Compares two completion items as they should be displayed.
+   * See ide_completion_results_invalidate_sort() to invalide the
+   * current sort settings.
+   */
+  gint (*compare) (IdeCompletionResults *self,
+                   IdeCompletionItem    *left,
+                   IdeCompletionItem    *right);
+};
+
+IdeCompletionResults *ide_completion_results_new              (const gchar                 *query);
+const gchar          *ide_completion_results_get_query        (IdeCompletionResults        *self);
+void                  ide_completion_results_invalidate_sort  (IdeCompletionResults        *self);
+void                  ide_completion_results_take_proposal    (IdeCompletionResults        *self,
+                                                               IdeCompletionItem           *proposal);
+void                  ide_completion_results_present          (IdeCompletionResults        *self,
+                                                               GtkSourceCompletionProvider *provider,
+                                                               GtkSourceCompletionContext  *context);
+gboolean              ide_completion_results_replay           (IdeCompletionResults        *self,
+                                                               const gchar                 *query);
+
+G_END_DECLS
+
+#endif /* IDE_COMPLETION_RESULTS_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]