[gnome-builder/wip/chergert/suggestion] wip on DzlSuggestionEntry



commit db99d9c7290b88dd726ec24f5a209fee96266722
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jun 7 18:06:05 2017 -0700

    wip on DzlSuggestionEntry

 libide/ide.h                                       |    3 +-
 libide/meson.build                                 |   15 +-
 libide/resources/libide.gresource.xml              |    2 -
 libide/search/ide-omni-search-display.c            |  678 --------------------
 libide/search/ide-omni-search-display.h            |   41 --
 libide/search/ide-omni-search-entry.c              |  403 ------------
 libide/search/ide-omni-search-entry.h              |   39 --
 libide/search/ide-omni-search-group.c              |  571 ----------------
 libide/search/ide-omni-search-group.h              |   50 --
 libide/search/ide-omni-search-group.ui             |   14 -
 libide/search/ide-omni-search-row.c                |  189 ------
 libide/search/ide-omni-search-row.ui               |   26 -
 libide/search/ide-search-context.c                 |  263 --------
 libide/search/ide-search-context.h                 |   50 --
 libide/search/ide-search-engine.c                  |  294 +++++++--
 libide/search/ide-search-engine.h                  |   14 +-
 libide/search/ide-search-entry.c                   |  102 +++
 .../{ide-omni-search-row.h => ide-search-entry.h}  |   22 +-
 libide/search/ide-search-provider.c                |  140 ++---
 libide/search/ide-search-provider.h                |   52 +-
 libide/search/ide-search-reducer.c                 |   95 +++-
 libide/search/ide-search-reducer.h                 |   28 +-
 libide/search/ide-search-result.c                  |  340 ++++-------
 libide/search/ide-search-result.h                  |   36 +-
 libide/workbench/ide-workbench-header-bar.c        |   14 +-
 libide/workbench/ide-workbench-header-bar.ui       |    2 +-
 plugins/file-search/gb-file-search-index.c         |   42 +-
 plugins/file-search/gb-file-search-index.h         |   33 +-
 plugins/file-search/gb-file-search-provider.c      |   75 +--
 29 files changed, 725 insertions(+), 2908 deletions(-)
---
diff --git a/libide/ide.h b/libide/ide.h
index 8e5a8f3..5065986 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -106,9 +106,8 @@ G_BEGIN_DECLS
 #include "runtimes/ide-runtime-manager.h"
 #include "runtimes/ide-runtime-provider.h"
 #include "runtimes/ide-runtime.h"
-#include "search/ide-omni-search-row.h"
-#include "search/ide-search-context.h"
 #include "search/ide-search-engine.h"
+#include "search/ide-search-entry.h"
 #include "search/ide-search-provider.h"
 #include "search/ide-search-reducer.h"
 #include "search/ide-search-result.h"
diff --git a/libide/meson.build b/libide/meson.build
index e96ca2d..9943ab1 100644
--- a/libide/meson.build
+++ b/libide/meson.build
@@ -122,14 +122,9 @@ libide_public_headers = [
   'runtimes/ide-runtime-manager.h',
   'runtimes/ide-runtime-provider.h',
   'runtimes/ide-runtime.h',
-  'search/ide-omni-search-display.h',
-  'search/ide-omni-search-entry.h',
-  'search/ide-omni-search-group.h',
-  'search/ide-omni-search-row.h',
-  'search/ide-search-context.h',
   'search/ide-search-engine.h',
+  'search/ide-search-entry.h',
   'search/ide-search-provider.h',
-  'search/ide-search-reducer.h',
   'search/ide-search-result.h',
   'snippets/ide-source-snippet-chunk.h',
   'snippets/ide-source-snippet-context.h',
@@ -321,12 +316,8 @@ libide_public_sources = [
   'runtimes/ide-runtime-manager.c',
   'runtimes/ide-runtime-provider.c',
   'runtimes/ide-runtime.c',
-  'search/ide-omni-search-display.c',
-  'search/ide-omni-search-entry.c',
-  'search/ide-omni-search-group.c',
-  'search/ide-omni-search-row.c',
-  'search/ide-search-context.c',
   'search/ide-search-engine.c',
+  'search/ide-search-entry.c',
   'search/ide-search-provider.c',
   'search/ide-search-result.c',
   'snippets/ide-source-snippet-chunk.c',
@@ -502,6 +493,7 @@ libide_sources = libide_generated_headers + libide_public_sources + [
   'preferences/ide-preferences-language-row.h',
   'runner/ide-run-manager-private.h',
   'search/ide-search-reducer.c',
+  'search/ide-search-reducer.h',
   'snippets/ide-source-snippet-completion-item.c',
   'snippets/ide-source-snippet-completion-item.h',
   'snippets/ide-source-snippet-completion-provider.c',
@@ -663,6 +655,7 @@ libide_plugin_dep = declare_dependency(
                          libgd_dep,
                          libgio_dep,
                          libgtk_dep,
+                         libgd_dep,
                          libgtksource_dep,
                          libtemplate_glib_dep,
                          libjson_glib_dep,
diff --git a/libide/resources/libide.gresource.xml b/libide/resources/libide.gresource.xml
index ba3f08c..2b79289 100644
--- a/libide/resources/libide.gresource.xml
+++ b/libide/resources/libide.gresource.xml
@@ -64,8 +64,6 @@
     <file compressed="true" alias="ide-layout-stack.ui">../workbench/ide-layout-stack.ui</file>
     <file compressed="true" alias="ide-omni-bar.ui">../workbench/ide-omni-bar.ui</file>
     <file compressed="true" alias="ide-omni-bar-row.ui">../workbench/ide-omni-bar-row.ui</file>
-    <file compressed="true" alias="ide-omni-search-group.ui">../search/ide-omni-search-group.ui</file>
-    <file compressed="true" alias="ide-omni-search-row.ui">../search/ide-omni-search-row.ui</file>
     <file compressed="true" 
alias="ide-perspective-menu-button.ui">../workbench/ide-perspective-menu-button.ui</file>
     <file compressed="true" 
alias="ide-preferences-language-row.ui">../preferences/ide-preferences-language-row.ui</file>
     <file compressed="true" alias="ide-run-button.ui">../runner/ide-run-button.ui</file>
diff --git a/libide/search/ide-search-engine.c b/libide/search/ide-search-engine.c
index 43e47b5..8c35476 100644
--- a/libide/search/ide-search-engine.c
+++ b/libide/search/ide-search-engine.c
@@ -1,6 +1,6 @@
 /* ide-search-engine.c
  *
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015-2017 Christian Hergert <chergert redhat com>
  *
  * 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
@@ -16,71 +16,61 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <glib/gi18n.h>
-#include <libpeas/peas.h>
+#define G_LOG_DOMAIN "ide-search-engine"
 
-#include "ide-internal.h"
+#include <libpeas/peas.h>
 
-#include "plugins/ide-extension-util.h"
-#include "search/ide-search-context.h"
-#include "search/ide-search-engine.h"
-#include "search/ide-search-provider.h"
-#include "search/ide-search-result.h"
+#include "ide-search-engine.h"
+#include "ide-search-provider.h"
+#include "ide-search-result.h"
 
 struct _IdeSearchEngine
 {
   IdeObject         parent_instance;
-
   PeasExtensionSet *extensions;
+  guint             active_count;
 };
 
-G_DEFINE_TYPE (IdeSearchEngine, ide_search_engine, IDE_TYPE_OBJECT)
-
-static void
-add_provider_to_context (PeasExtensionSet *extensions,
-                         PeasPluginInfo   *plugin_info,
-                         PeasExtension    *extension,
-                         IdeSearchContext *search_context)
+typedef struct
 {
-  g_assert (PEAS_IS_EXTENSION_SET (extensions));
-  g_assert (plugin_info != NULL);
-  g_assert (IDE_IS_SEARCH_PROVIDER (extension));
-  g_assert (IDE_IS_SEARCH_CONTEXT (search_context));
+  GTask         *task;
+  gchar         *query;
+  GListStore    *store;
+  guint          outstanding;
+  guint          max_results;
+} Request;
 
-  _ide_search_context_add_provider (search_context, IDE_SEARCH_PROVIDER (extension), 0);
-}
+enum {
+  PROP_0,
+  PROP_BUSY,
+  N_PROPS
+};
 
-/**
- * ide_search_engine_search:
- * @search_terms: The search terms.
- *
- * Begins a query against the requested search providers.
- *
- * If @providers is %NULL, all registered providers will be used.
- *
- * Returns: (transfer full) (nullable): An #IdeSearchContext or %NULL if no
- *   providers could be loaded.
- */
-IdeSearchContext *
-ide_search_engine_search (IdeSearchEngine *self,
-                          const gchar     *search_terms)
-{
-  IdeSearchContext *search_context;
-  IdeContext *context;
+G_DEFINE_TYPE (IdeSearchEngine, ide_search_engine, IDE_TYPE_OBJECT)
 
-  g_return_val_if_fail (IDE_IS_SEARCH_ENGINE (self), NULL);
-  g_return_val_if_fail (search_terms, NULL);
+static GParamSpec *properties [N_PROPS];
 
-  context = ide_object_get_context (IDE_OBJECT (self));
-  search_context = g_object_new (IDE_TYPE_SEARCH_CONTEXT,
-                                 "context", context,
-                                 NULL);
+static Request *
+request_new (void)
+{
+  Request *r;
 
-  peas_extension_set_foreach (self->extensions,
-                              (PeasExtensionSetForeachFunc)add_provider_to_context,
-                              search_context);
+  r = g_slice_new0 (Request);
+  r->store = NULL;
+  r->outstanding = 0;
+  r->query = NULL;
+
+  return r;
+}
 
-  return search_context;
+static void
+request_destroy (Request *r)
+{
+  g_assert (r->outstanding == 0);
+  g_clear_object (&r->store);
+  g_clear_pointer (&r->query, g_free);
+  r->task = NULL;
+  g_slice_free (Request, r);
 }
 
 static void
@@ -89,14 +79,16 @@ ide_search_engine_constructed (GObject *object)
   IdeSearchEngine *self = (IdeSearchEngine *)object;
   IdeContext *context;
 
-  context = ide_object_get_context (IDE_OBJECT (self));
-
-  self->extensions = ide_extension_set_new (peas_engine_get_default (),
-                                            IDE_TYPE_SEARCH_PROVIDER,
-                                            "context", context,
-                                            NULL);
+  g_assert (IDE_IS_SEARCH_ENGINE (self));
 
   G_OBJECT_CLASS (ide_search_engine_parent_class)->constructed (object);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+
+  self->extensions = peas_extension_set_new (peas_engine_get_default (),
+                                             IDE_TYPE_SEARCH_PROVIDER,
+                                             "context", context,
+                                             NULL);
 }
 
 static void
@@ -110,15 +102,205 @@ ide_search_engine_dispose (GObject *object)
 }
 
 static void
+ide_search_engine_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+  IdeSearchEngine *self = IDE_SEARCH_ENGINE (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUSY:
+      g_value_set_boolean (value, ide_search_engine_get_busy (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
 ide_search_engine_class_init (IdeSearchEngineClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   object_class->constructed = ide_search_engine_constructed;
   object_class->dispose = ide_search_engine_dispose;
+  object_class->get_property = ide_search_engine_get_property;
+
+  properties [PROP_BUSY] =
+    g_param_spec_boolean ("busy",
+                          "Busy",
+                          "If the search engine is busy",
+                          FALSE,
+                          (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
 }
 
 static void
 ide_search_engine_init (IdeSearchEngine *self)
 {
 }
+
+IdeSearchEngine *
+ide_search_engine_new (void)
+{
+  return g_object_new (IDE_TYPE_SEARCH_ENGINE, NULL);
+}
+
+/**
+ * ide_search_engine_get_busy:
+ * @self: a #IdeSearchEngine
+ *
+ * Checks if the #IdeSearchEngine is currently executing a query.
+ *
+ * Returns: %TRUE if queries are being processed.
+ */
+gboolean
+ide_search_engine_get_busy (IdeSearchEngine *self)
+{
+  g_return_val_if_fail (IDE_IS_SEARCH_ENGINE (self), FALSE);
+
+  return self->active_count > 0;
+}
+
+static void
+ide_search_engine_search_cb (GObject      *object,
+                             GAsyncResult *result,
+                             gpointer      user_data)
+{
+  IdeSearchProvider *provider = (IdeSearchProvider *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GPtrArray) ar = NULL;
+  Request *r;
+
+  g_assert (IDE_IS_SEARCH_PROVIDER (provider));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  r = g_task_get_task_data (task);
+  g_assert (r != NULL);
+  g_assert (r->task == task);
+  g_assert (r->outstanding > 0);
+  g_assert (G_IS_LIST_STORE (r->store));
+
+  ar = ide_search_provider_search_finish (provider, result, &error);
+
+  if (error != NULL)
+    {
+      g_warning ("%s", error->message);
+      goto cleanup;
+    }
+
+  for (guint i = 0; i < ar->len; i++)
+    {
+      IdeSearchResult *item = g_ptr_array_index (ar, i);
+
+      g_assert (IDE_IS_SEARCH_RESULT (item));
+
+      g_list_store_insert_sorted (r->store,
+                                  item,
+                                  (GCompareDataFunc)ide_search_result_compare,
+                                  NULL);
+
+    }
+
+cleanup:
+  r->outstanding--;
+
+  if (r->outstanding == 0)
+    g_task_return_pointer (task, g_steal_pointer (&r->store), g_object_unref);
+}
+
+static void
+ide_search_engine_search_foreach (PeasExtensionSet *set,
+                                  PeasPluginInfo   *plugin_info,
+                                  PeasExtension    *exten,
+                                  gpointer          user_data)
+{
+  IdeSearchProvider *provider = (IdeSearchProvider *)exten;
+  Request *r = user_data;
+
+  g_assert (PEAS_IS_EXTENSION_SET (set));
+  g_assert (plugin_info != NULL);
+  g_assert (IDE_IS_SEARCH_PROVIDER (provider));
+  g_assert (r != NULL);
+  g_assert (G_IS_TASK (r->task));
+  g_assert (G_IS_LIST_STORE (r->store));
+
+  r->outstanding++;
+
+  ide_search_provider_search_async (provider,
+                                    r->query,
+                                    r->max_results,
+                                    g_task_get_cancellable (r->task),
+                                    ide_search_engine_search_cb,
+                                    g_object_ref (r->task));
+}
+
+void
+ide_search_engine_search_async (IdeSearchEngine     *self,
+                                const gchar         *query,
+                                GCancellable        *cancellable,
+                                GAsyncReadyCallback  callback,
+                                gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GListStore) store = NULL;
+  Request *r;
+
+  g_return_if_fail (IDE_IS_SEARCH_ENGINE (self));
+  g_return_if_fail (query != NULL);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_search_engine_search_async);
+  g_task_set_priority (task, G_PRIORITY_LOW);
+
+  r = request_new ();
+  r->query = g_strdup (query);
+  r->max_results = 25;
+  r->task = task;
+  r->store = g_list_store_new (IDE_TYPE_SEARCH_RESULT);
+  r->outstanding = 0;
+  g_task_set_task_data (task, r, (GDestroyNotify)request_destroy);
+
+  peas_extension_set_foreach (self->extensions,
+                              ide_search_engine_search_foreach,
+                              r);
+
+  self->active_count += r->outstanding;
+
+  if (r->outstanding == 0)
+    g_task_return_pointer (task,
+                           g_object_ref (r->store),
+                           g_object_unref);
+
+  g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BUSY]);
+}
+
+/**
+ * ide_search_engine_search_finish:
+ * @self: a #IdeSearchEngine
+ * @result: a #GAsyncResult
+ * @error: a location for a #GError, or %NULL
+ *
+ * Completes an asynchronous request to ide_search_engine_search_async().
+ *
+ * The result is a #GListModel of #IdeSearchResult when successful.
+ *
+ * Returns: (transfer full): A #GListModel of #IdeSearchResult items.
+ */
+GListModel *
+ide_search_engine_search_finish (IdeSearchEngine  *self,
+                                 GAsyncResult     *result,
+                                 GError          **error)
+{
+  g_return_val_if_fail (IDE_IS_SEARCH_ENGINE (self), NULL);
+  g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
diff --git a/libide/search/ide-search-engine.h b/libide/search/ide-search-engine.h
index 38dab7a..2f14b1a 100644
--- a/libide/search/ide-search-engine.h
+++ b/libide/search/ide-search-engine.h
@@ -1,6 +1,6 @@
 /* ide-search-engine.h
  *
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015-2017 Christian Hergert <chergert redhat com>
  *
  * 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
@@ -27,8 +27,16 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeSearchEngine, ide_search_engine, IDE, SEARCH_ENGINE, IdeObject)
 
-IdeSearchContext *ide_search_engine_search (IdeSearchEngine   *self,
-                                            const gchar       *search_terms);
+IdeSearchEngine *ide_search_engine_new           (void);
+gboolean         ide_search_engine_get_busy      (IdeSearchEngine      *self);
+void             ide_search_engine_search_async  (IdeSearchEngine      *self,
+                                                  const gchar          *query,
+                                                  GCancellable         *cancellable,
+                                                  GAsyncReadyCallback   callback,
+                                                  gpointer              user_data);
+GListModel      *ide_search_engine_search_finish (IdeSearchEngine      *self,
+                                                  GAsyncResult         *result,
+                                                  GError              **error);
 
 G_END_DECLS
 
diff --git a/libide/search/ide-search-entry.c b/libide/search/ide-search-entry.c
new file mode 100644
index 0000000..a306dda
--- /dev/null
+++ b/libide/search/ide-search-entry.c
@@ -0,0 +1,102 @@
+/* ide-search-entry.c
+ *
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
+ *
+ * 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-search-entry"
+
+#include "ide-context.h"
+#include "ide-macros.h"
+
+#include "search/ide-search-engine.h"
+#include "search/ide-search-entry.h"
+#include "util/ide-gtk.h"
+
+struct _IdeSearchEntry
+{
+  DzlSuggestionEntry parent_instance;
+};
+
+G_DEFINE_TYPE (IdeSearchEntry, ide_search_entry, DZL_TYPE_SUGGESTION_ENTRY)
+
+static void
+ide_search_entry_search_cb (GObject      *object,
+                            GAsyncResult *result,
+                            gpointer      user_data)
+{
+  IdeSearchEngine *engine = (IdeSearchEngine *)object;
+  g_autoptr(IdeSearchEntry) self = user_data;
+  g_autoptr(GListModel) suggestions = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_SEARCH_ENTRY (self));
+  g_assert (IDE_IS_SEARCH_ENGINE (engine));
+
+  suggestions = ide_search_engine_search_finish (engine, result, &error);
+
+  if (error != NULL)
+    {
+      /* TODO: Elevate to workbench message once we have that capability */
+      g_warning ("%s", error->message);
+      return;
+    }
+
+  g_assert (suggestions != NULL);
+  g_assert (G_IS_LIST_MODEL (suggestions));
+  g_assert (g_type_is_a (g_list_model_get_item_type (suggestions), DZL_TYPE_SUGGESTION));
+
+  dzl_suggestion_entry_set_model (DZL_SUGGESTION_ENTRY (self), suggestions);
+}
+
+static void
+ide_search_entry_changed (IdeSearchEntry *self)
+{
+  IdeSearchEngine *engine;
+  IdeContext *context;
+  const gchar *typed_text;
+
+  g_assert (IDE_IS_SEARCH_ENTRY (self));
+
+  if (NULL == (context = ide_widget_get_context (GTK_WIDGET (self))))
+    return;
+
+  typed_text = dzl_suggestion_entry_get_typed_text (DZL_SUGGESTION_ENTRY (self));
+
+  if (ide_str_empty0 (typed_text))
+    {
+      dzl_suggestion_entry_set_model (DZL_SUGGESTION_ENTRY (self), NULL);
+      return;
+    }
+
+  engine = ide_context_get_search_engine (context);
+
+  ide_search_engine_search_async (engine,
+                                  typed_text,
+                                  NULL,
+                                  ide_search_entry_search_cb,
+                                  g_object_ref (self));
+}
+
+static void
+ide_search_entry_class_init (IdeSearchEntryClass *klass)
+{
+}
+
+static void
+ide_search_entry_init (IdeSearchEntry *self)
+{
+  g_signal_connect (self, "changed", G_CALLBACK (ide_search_entry_changed), NULL);
+}
diff --git a/libide/search/ide-omni-search-row.h b/libide/search/ide-search-entry.h
similarity index 52%
rename from libide/search/ide-omni-search-row.h
rename to libide/search/ide-search-entry.h
index 2e950ec..e594d1e 100644
--- a/libide/search/ide-omni-search-row.h
+++ b/libide/search/ide-search-entry.h
@@ -1,6 +1,6 @@
-/* ide-omni-search-row.h
+/* ide-search-entry.h
  *
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015-2017 Christian Hergert <chergert redhat com>
  *
  * 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
@@ -16,23 +16,19 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef IDE_OMNI_SEARCH_ROW_H
-#define IDE_OMNI_SEARCH_ROW_H
+#ifndef IDE_SEARCH_ENTRY_H
+#define IDE_SEARCH_ENTRY_H
 
-#include <gtk/gtk.h>
-
-#include "ide-search-result.h"
+#include <dazzle.h>
 
 G_BEGIN_DECLS
 
-#define IDE_TYPE_OMNI_SEARCH_ROW (ide_omni_search_row_get_type())
+#define IDE_TYPE_SEARCH_ENTRY (ide_search_entry_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeOmniSearchRow, ide_omni_search_row, IDE, OMNI_SEARCH_ROW, GtkListBoxRow)
+G_DECLARE_FINAL_TYPE (IdeSearchEntry, ide_search_entry, IDE, SEARCH_ENTRY, DzlSuggestionEntry)
 
-IdeSearchResult *ide_omni_search_row_get_result (IdeOmniSearchRow *row);
-void             ide_omni_search_row_set_result (IdeOmniSearchRow *row,
-                                                 IdeSearchResult  *result);
+GtkWidget *ide_search_entry_new (void);
 
 G_END_DECLS
 
-#endif /* IDE_OMNI_SEARCH_ROW_H */
+#endif /* IDE_SEARCH_ENTRY_H */
diff --git a/libide/search/ide-search-provider.c b/libide/search/ide-search-provider.c
index 187d607..0924e1c 100644
--- a/libide/search/ide-search-provider.c
+++ b/libide/search/ide-search-provider.c
@@ -1,6 +1,6 @@
 /* ide-search-provider.c
  *
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
  *
  * 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
@@ -16,127 +16,83 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <glib/gi18n.h>
+#define G_LOG_DOMAIN "ide-search-provider"
 
-#include "ide-context.h"
-#include "ide-search-context.h"
 #include "ide-search-provider.h"
-#include "ide-search-result.h"
 
 G_DEFINE_INTERFACE (IdeSearchProvider, ide_search_provider, IDE_TYPE_OBJECT)
 
-static const gchar *
-ide_search_provider_real_get_verb (IdeSearchProvider *self)
+static void
+ide_search_provider_real_search_async (IdeSearchProvider   *self,
+                                       const gchar         *query,
+                                       guint                max_results,
+                                       GCancellable        *cancellable,
+                                       GAsyncReadyCallback  callback,
+                                       gpointer             user_data)
 {
-  return "";
-}
+  g_autoptr(GTask) task = NULL;
 
-static gint
-ide_search_provider_real_get_priority (IdeSearchProvider *self)
-{
-  return -1;
-}
+  g_assert (IDE_IS_SEARCH_PROVIDER (self));
+  g_assert (query != NULL);
 
-static gunichar
-ide_search_provider_real_get_prefix (IdeSearchProvider *self)
-{
-  return '\0';
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_return_new_error (task,
+                           G_IO_ERROR,
+                           G_IO_ERROR_NOT_SUPPORTED,
+                           "search not implemented");
 }
 
-static GtkWidget *
-ide_search_provider_real_create_row (IdeSearchProvider *self,
-                                     IdeSearchResult   *result)
+static GPtrArray *
+ide_search_provider_real_search_finish (IdeSearchProvider  *self,
+                                        GAsyncResult       *result,
+                                        GError            **error)
 {
-  return NULL;
-}
+  g_assert (IDE_IS_SEARCH_PROVIDER (self));
+  g_assert (G_IS_TASK (result));
 
-static void
-ide_search_provider_real_activate (IdeSearchProvider *self,
-                                   GtkWidget         *row,
-                                   IdeSearchResult   *result)
-{
+  return g_task_propagate_pointer (G_TASK (result), error);
 }
 
 static void
 ide_search_provider_default_init (IdeSearchProviderInterface *iface)
 {
-  iface->get_verb = ide_search_provider_real_get_verb;
-  iface->get_priority = ide_search_provider_real_get_priority;
-  iface->get_prefix = ide_search_provider_real_get_prefix;
-  iface->create_row = ide_search_provider_real_create_row;
-  iface->activate = ide_search_provider_real_activate;
-}
-
-const gchar *
-ide_search_provider_get_verb (IdeSearchProvider *provider)
-{
-  g_return_val_if_fail (IDE_IS_SEARCH_PROVIDER (provider), NULL);
-
-  return IDE_SEARCH_PROVIDER_GET_IFACE (provider)->get_verb (provider);
-}
-
-gint
-ide_search_provider_get_priority (IdeSearchProvider *provider)
-{
-  g_return_val_if_fail (IDE_IS_SEARCH_PROVIDER (provider), -1);
-
-  return IDE_SEARCH_PROVIDER_GET_IFACE (provider)->get_priority (provider);
-}
-
-gunichar
-ide_search_provider_get_prefix (IdeSearchProvider *provider)
-{
-  g_return_val_if_fail (IDE_IS_SEARCH_PROVIDER (provider), -1);
-
-  return IDE_SEARCH_PROVIDER_GET_IFACE (provider)->get_prefix (provider);
+  iface->search_async = ide_search_provider_real_search_async;
+  iface->search_finish = ide_search_provider_real_search_finish;
 }
 
 void
-ide_search_provider_populate (IdeSearchProvider *provider,
-                              IdeSearchContext  *context,
-                              const gchar       *search_terms,
-                              gsize              max_results,
-                              GCancellable      *cancellable)
+ide_search_provider_search_async (IdeSearchProvider   *self,
+                                  const gchar         *query,
+                                  guint                max_results,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
 {
-  g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
-  g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
-  g_return_if_fail (search_terms != NULL);
+  g_return_if_fail (IDE_IS_SEARCH_PROVIDER (self));
+  g_return_if_fail (query != NULL);
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  return IDE_SEARCH_PROVIDER_GET_IFACE (provider)->populate (provider,
-                                                             context,
-                                                             search_terms,
-                                                             max_results,
-                                                             cancellable);
+  IDE_SEARCH_PROVIDER_GET_IFACE (self)->search_async (self, query, max_results, cancellable, callback, 
user_data);
 }
 
 /**
- * ide_search_provider_create_row:
- * @provider: A #IdeSearchProvider.
- * @result: A #IdeSearchResult.
+ * ide_search_provider_search_finish:
+ * @self: a #IdeSearchProvider
+ * @result: a #GAsyncResult
+ * @error: a location for a #GError, or %NULL
  *
- * Create a row to display the search result.
+ * Completes a request to a search provider.
  *
- * Returns: (transfer full): A #GtkWidget.
+ * Returns: (transfer container) (element-type Ide.SearchResult): A #GPtrArray
+ *    of #IdeSearchResult elements.
  */
-GtkWidget *
-ide_search_provider_create_row (IdeSearchProvider *self,
-                                IdeSearchResult   *result)
+GPtrArray *
+ide_search_provider_search_finish (IdeSearchProvider  *self,
+                                   GAsyncResult       *result,
+                                   GError            **error)
 {
   g_return_val_if_fail (IDE_IS_SEARCH_PROVIDER (self), NULL);
-  g_return_val_if_fail (IDE_IS_SEARCH_RESULT (result), NULL);
-
-  return IDE_SEARCH_PROVIDER_GET_IFACE (self)->create_row (self, result);
-}
-
-void
-ide_search_provider_activate (IdeSearchProvider *self,
-                              GtkWidget         *row,
-                              IdeSearchResult   *result)
-{
-  g_return_if_fail (IDE_IS_SEARCH_PROVIDER (self));
-  g_return_if_fail (GTK_IS_WIDGET (row));
-  g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
 
-  return IDE_SEARCH_PROVIDER_GET_IFACE (self)->activate (self, row, result);
+  return IDE_SEARCH_PROVIDER_GET_IFACE (self)->search_finish (self, result, error);
 }
diff --git a/libide/search/ide-search-provider.h b/libide/search/ide-search-provider.h
index c6f3448..6000cf9 100644
--- a/libide/search/ide-search-provider.h
+++ b/libide/search/ide-search-provider.h
@@ -1,6 +1,6 @@
 /* ide-search-provider.h
  *
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * Copyright (C) 2015-2017 Christian Hergert <chergert redhat com>
  *
  * 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
@@ -19,8 +19,6 @@
 #ifndef IDE_SEARCH_PROVIDER_H
 #define IDE_SEARCH_PROVIDER_H
 
-#include <gtk/gtk.h>
-
 #include "ide-object.h"
 
 G_BEGIN_DECLS
@@ -31,36 +29,28 @@ G_DECLARE_INTERFACE (IdeSearchProvider, ide_search_provider, IDE, SEARCH_PROVIDE
 
 struct _IdeSearchProviderInterface
 {
-  GTypeInterface parent_iface;
-
-  gunichar     (*get_prefix)   (IdeSearchProvider *provider);
-  gint         (*get_priority) (IdeSearchProvider *provider);
-  const gchar *(*get_verb)     (IdeSearchProvider *provider);
-  void         (*populate)     (IdeSearchProvider *provider,
-                                IdeSearchContext  *context,
-                                const gchar       *search_terms,
-                                gsize              max_results,
-                                GCancellable      *cancellable);
-  GtkWidget  *(*create_row)    (IdeSearchProvider *provider,
-                                IdeSearchResult   *result);
-  void        (*activate)      (IdeSearchProvider *provider,
-                                GtkWidget         *row,
-                                IdeSearchResult   *result);
+  GTypeInterface parent_interface;
+
+  void       (*search_async)  (IdeSearchProvider    *self,
+                               const gchar          *query,
+                               guint                 max_results,
+                               GCancellable         *cancellable,
+                               GAsyncReadyCallback   callback,
+                               gpointer              user_data);
+  GPtrArray *(*search_finish) (IdeSearchProvider    *self,
+                               GAsyncResult         *result,
+                               GError              **error);
 };
 
-gunichar     ide_search_provider_get_prefix   (IdeSearchProvider *provider);
-gint         ide_search_provider_get_priority (IdeSearchProvider *provider);
-const gchar *ide_search_provider_get_verb     (IdeSearchProvider *provider);
-void         ide_search_provider_populate     (IdeSearchProvider *provider,
-                                               IdeSearchContext  *context,
-                                               const gchar       *search_terms,
-                                               gsize              max_results,
-                                               GCancellable      *cancellable);
-GtkWidget   *ide_search_provider_create_row   (IdeSearchProvider *provider,
-                                               IdeSearchResult   *result);
-void         ide_search_provider_activate     (IdeSearchProvider *provider,
-                                               GtkWidget         *row,
-                                               IdeSearchResult   *result);
+void       ide_search_provider_search_async  (IdeSearchProvider    *self,
+                                              const gchar          *query,
+                                              guint                 max_results,
+                                              GCancellable         *cancellable,
+                                              GAsyncReadyCallback   callback,
+                                              gpointer              user_data);
+GPtrArray *ide_search_provider_search_finish (IdeSearchProvider    *self,
+                                              GAsyncResult         *result,
+                                              GError              **error);
 
 G_END_DECLS
 
diff --git a/libide/search/ide-search-reducer.c b/libide/search/ide-search-reducer.c
index 5d765de..e38209e 100644
--- a/libide/search/ide-search-reducer.c
+++ b/libide/search/ide-search-reducer.c
@@ -16,61 +16,114 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include "ide-search-context.h"
-#include "ide-search-provider.h"
+#define G_LOG_DOMAIN "ide-search-reducer"
+
 #include "ide-search-reducer.h"
 #include "ide-search-result.h"
 
+#define DEFAULT_MAX_ITEMS 1000
+
 void
 ide_search_reducer_init (IdeSearchReducer  *reducer,
-                         IdeSearchContext  *context,
-                         IdeSearchProvider *provider,
                          gsize              max_results)
 {
   g_return_if_fail (reducer);
-  g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
-  g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
 
-  reducer->context = context;
-  reducer->provider = provider;
   reducer->sequence = g_sequence_new (g_object_unref);
-  reducer->max_results = max_results ?: G_MAXSIZE;
+  reducer->max_results = max_results ? max_results : DEFAULT_MAX_ITEMS;
   reducer->count = 0;
 }
 
 void
 ide_search_reducer_destroy (IdeSearchReducer *reducer)
 {
-  g_return_if_fail (reducer);
+  g_return_if_fail (reducer != NULL);
 
-  if (reducer->sequence)
+  if (reducer->sequence != NULL)
     g_sequence_free (reducer->sequence);
 }
 
+GPtrArray *
+ide_search_reducer_free (IdeSearchReducer *reducer,
+                         gboolean          free_results)
+{
+  GPtrArray *ar;
+  GSequenceIter *iter;
+  GSequenceIter *end;
+
+  g_return_val_if_fail (reducer != NULL, NULL);
+
+  if (free_results)
+    {
+      ide_search_reducer_destroy (reducer);
+      return NULL;
+    }
+
+  ar = g_ptr_array_new_with_free_func (g_object_unref);
+
+  end = g_sequence_get_end_iter (reducer->sequence);
+
+  for (iter = g_sequence_get_begin_iter (reducer->sequence);
+       iter != end;
+       iter = g_sequence_iter_next (iter))
+    {
+      IdeSearchResult *result = g_sequence_get (iter);
+      g_ptr_array_add (ar, g_object_ref (result));
+    }
+
+  g_sequence_free (reducer->sequence);
+
+  reducer->sequence = NULL;
+  reducer->max_results = 0;
+  reducer->count = 0;
+
+  return ar;
+}
+
+/**
+ * ide_search_reducer_take:
+ * @self: an #IdeSearchReducer
+ * @result: (transfer full): an #IdeSearchResult
+ *
+ * Like ide_search_reducer_push() but takes ownership of @result by
+ * stealing the reference.
+ */
 void
-ide_search_reducer_push (IdeSearchReducer *reducer,
+ide_search_reducer_take (IdeSearchReducer *reducer,
                          IdeSearchResult  *result)
 {
-  g_return_if_fail (reducer);
-  g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+  g_assert (reducer != NULL);
+  g_assert (IDE_IS_SEARCH_RESULT (result));
 
-  if (reducer->max_results <= g_sequence_get_length (reducer->sequence))
+  if (reducer->count == reducer->max_results)
     {
+      g_autoptr(IdeSearchResult) lowest = NULL;
       GSequenceIter *iter;
-      IdeSearchResult *lowest;
 
       /* Remove lowest score */
       iter = g_sequence_get_begin_iter (reducer->sequence);
       lowest = g_sequence_get (iter);
-      ide_search_context_remove_result (reducer->context, reducer->provider, lowest);
       g_sequence_remove (iter);
     }
+  else
+    {
+      reducer->count++;
+    }
 
   g_sequence_insert_sorted (reducer->sequence,
-                            g_object_ref (result),
+                            result,
                             (GCompareDataFunc)ide_search_result_compare,
                             NULL);
-  ide_search_context_add_result (reducer->context, reducer->provider, result);
+}
+
+void
+ide_search_reducer_push (IdeSearchReducer *reducer,
+                         IdeSearchResult  *result)
+{
+  g_assert (reducer != NULL);
+  g_assert (IDE_IS_SEARCH_RESULT (result));
+
+  ide_search_reducer_take (reducer, g_object_ref (result));
 }
 
 gboolean
@@ -81,12 +134,12 @@ ide_search_reducer_accepts (IdeSearchReducer *reducer,
 
   g_return_val_if_fail (reducer, FALSE);
 
-  if (g_sequence_get_length (reducer->sequence) < reducer->max_results)
+  if (reducer->count < reducer->max_results)
     return TRUE;
 
   iter = g_sequence_get_begin_iter (reducer->sequence);
 
-  if (iter)
+  if (iter != NULL)
     {
       IdeSearchResult *result;
 
diff --git a/libide/search/ide-search-reducer.h b/libide/search/ide-search-reducer.h
index e67b689..a38a852 100644
--- a/libide/search/ide-search-reducer.h
+++ b/libide/search/ide-search-reducer.h
@@ -25,22 +25,22 @@ G_BEGIN_DECLS
 
 typedef struct
 {
-  IdeSearchContext  *context;
-  IdeSearchProvider *provider;
-  GSequence         *sequence;
-  gsize              max_results;
-  gsize              count;
+  GSequence *sequence;
+  gsize      max_results;
+  gsize      count;
 } IdeSearchReducer;
 
-void     ide_search_reducer_init    (IdeSearchReducer  *reducer,
-                                     IdeSearchContext  *context,
-                                     IdeSearchProvider *provider,
-                                     gsize              max_results);
-gboolean ide_search_reducer_accepts (IdeSearchReducer  *reducer,
-                                     gfloat             score);
-void     ide_search_reducer_push    (IdeSearchReducer  *reducer,
-                                     IdeSearchResult   *result);
-void     ide_search_reducer_destroy (IdeSearchReducer  *reducer);
+void       ide_search_reducer_init    (IdeSearchReducer  *reducer,
+                                       gsize              max_results);
+gboolean   ide_search_reducer_accepts (IdeSearchReducer  *reducer,
+                                       gfloat             score);
+void       ide_search_reducer_take    (IdeSearchReducer  *reducer,
+                                       IdeSearchResult   *result);
+void       ide_search_reducer_push    (IdeSearchReducer  *reducer,
+                                       IdeSearchResult   *result);
+void       ide_search_reducer_destroy (IdeSearchReducer  *reducer);
+GPtrArray *ide_search_reducer_free    (IdeSearchReducer  *reducer,
+                                       gboolean           free_results);
 
 G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (IdeSearchReducer, ide_search_reducer_destroy)
 
diff --git a/libide/search/ide-search-result.c b/libide/search/ide-search-result.c
index 1fed0a2..0201f28 100644
--- a/libide/search/ide-search-result.c
+++ b/libide/search/ide-search-result.c
@@ -1,6 +1,6 @@
 /* ide-search-result.c
  *
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
  *
  * 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
@@ -16,304 +16,182 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <glib/gi18n.h>
+#define G_LOG_DOMAIN "ide-search-result"
 
-#include "ide-context.h"
 #include "ide-search-result.h"
 
 typedef struct
 {
-  IdeSearchProvider *provider;
-  gchar             *title;
-  gchar             *subtitle;
-  gfloat             score;
+  gfloat score;
+  guint  priority;
 } IdeSearchResultPrivate;
 
-G_DEFINE_TYPE_WITH_PRIVATE (IdeSearchResult, ide_search_result, IDE_TYPE_OBJECT)
-
 enum {
   PROP_0,
-  PROP_PROVIDER,
   PROP_SCORE,
-  PROP_SUBTITLE,
-  PROP_TITLE,
-  LAST_PROP
+  PROP_PRIORITY,
+  N_PROPS
 };
 
-static GParamSpec *properties [LAST_PROP];
-
-IdeSearchResult *
-ide_search_result_new (IdeSearchProvider *provider,
-                       const gchar       *title,
-                       const gchar       *subtitle,
-                       gfloat             score)
-{
-  IdeSearchResult *self;
-  IdeContext *context;
-
-  g_return_val_if_fail (IDE_IS_SEARCH_PROVIDER (provider), NULL);
+G_DEFINE_TYPE_WITH_PRIVATE (IdeSearchResult, ide_search_result, DZL_TYPE_SUGGESTION)
 
-  context = ide_object_get_context (IDE_OBJECT (provider));
+static GParamSpec *properties [N_PROPS];
 
-  self = g_object_new (IDE_TYPE_SEARCH_RESULT,
-                       "context", context,
-                       "provider", provider,
-                       "score", score,
-                       "subtitle", subtitle,
-                       "title", title,
-                       NULL);
-
-  return self;
-}
-
-/**
- * ide_search_result_get_provider:
- * @result: A #IdeSearchResult.
- *
- * Gets the provider that created the search result.
- *
- * Returns: (transfer none): An #IdeSearchProvider.
- */
-IdeSearchProvider *
-ide_search_result_get_provider (IdeSearchResult *self)
+static void
+ide_search_result_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
 {
-  IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
+  IdeSearchResult *self = IDE_SEARCH_RESULT (object);
 
-  g_return_val_if_fail (IDE_IS_SEARCH_RESULT (self), NULL);
+  switch (prop_id)
+    {
+    case PROP_SCORE:
+      g_value_set_float (value, ide_search_result_get_score (self));
+      break;
+
+    case PROP_PRIORITY:
+      g_value_set_int (value, ide_search_result_get_priority (self));
+      break;
 
-  return priv->provider;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
 }
 
 static void
-ide_search_result_set_provider (IdeSearchResult   *self,
-                                IdeSearchProvider *provider)
+ide_search_result_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
 {
-  IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
+  IdeSearchResult *self = IDE_SEARCH_RESULT (object);
 
-  g_return_if_fail (IDE_IS_SEARCH_RESULT (self));
-  g_return_if_fail (!provider || IDE_IS_SEARCH_PROVIDER (provider));
+  switch (prop_id)
+    {
+    case PROP_SCORE:
+      ide_search_result_set_score (self, g_value_get_float (value));
+      break;
+
+    case PROP_PRIORITY:
+      ide_search_result_set_priority (self, g_value_get_int (value));
+      break;
 
-  g_set_object (&priv->provider, provider);
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
 }
 
-const gchar *
-ide_search_result_get_title (IdeSearchResult *self)
+static void
+ide_search_result_class_init (IdeSearchResultClass *klass)
 {
-  IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
-  g_return_val_if_fail (IDE_IS_SEARCH_RESULT (self), NULL);
+  object_class->get_property = ide_search_result_get_property;
+  object_class->set_property = ide_search_result_set_property;
 
-  return priv->title;
+  properties [PROP_SCORE] =
+    g_param_spec_float ("score",
+                        "Score",
+                        "The score of the result",
+                        -G_MINFLOAT,
+                        G_MAXFLOAT,
+                        0.0f,
+                        (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_PRIORITY] =
+    g_param_spec_int ("priority",
+                      "Priority",
+                      "The priority of search result group",
+                      G_MININT,
+                      G_MAXINT,
+                      0,
+                      (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
 }
 
-const gchar *
-ide_search_result_get_subtitle (IdeSearchResult *self)
+static void
+ide_search_result_init (IdeSearchResult *self)
 {
-  IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
-
-  g_return_val_if_fail (IDE_IS_SEARCH_RESULT (self), NULL);
-
-  return priv->subtitle;
 }
 
-gfloat
-ide_search_result_get_score (IdeSearchResult *self)
+IdeSearchResult *
+ide_search_result_new (void)
 {
-  IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
-
-  g_return_val_if_fail (IDE_IS_SEARCH_RESULT (self), 0.0);
-
-  return priv->score;
+  return g_object_new (IDE_TYPE_SEARCH_RESULT, NULL);
 }
 
-static void
-ide_search_result_set_title (IdeSearchResult *self,
-                             const gchar     *title)
+gint
+ide_search_result_compare (gconstpointer a,
+                           gconstpointer b)
 {
-  IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
+  IdeSearchResult *ra = (IdeSearchResult *)a;
+  IdeSearchResult *rb = (IdeSearchResult *)b;
+  IdeSearchResultPrivate *priva = ide_search_result_get_instance_private (ra);
+  IdeSearchResultPrivate *privb = ide_search_result_get_instance_private (rb);
+  gint ret;
 
-  g_return_if_fail (IDE_IS_SEARCH_RESULT (self));
+  ret = priva->priority - privb->priority;
 
-  if (priv->title != title)
+  if (ret == 0)
     {
-      g_free (priv->title);
-      priv->title = g_strdup (title);
+      if (priva->score < privb->score)
+        priva->score = -1;
+      else if (priva->score > privb->score)
+        priva->score = 1;
     }
+
+  return ret;
 }
 
-static void
-ide_search_result_set_subtitle (IdeSearchResult *self,
-                                const gchar     *subtitle)
+gfloat
+ide_search_result_get_score (IdeSearchResult *self)
 {
   IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
 
-  g_return_if_fail (IDE_IS_SEARCH_RESULT (self));
+  g_return_val_if_fail (IDE_IS_SEARCH_RESULT (self), 0.0f);
 
-  if (priv->subtitle != subtitle)
-    {
-      g_free (priv->subtitle);
-      priv->subtitle = g_strdup (subtitle);
-    }
+  return priv->score;
 }
 
-static void
+void
 ide_search_result_set_score (IdeSearchResult *self,
                              gfloat           score)
 {
   IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
 
   g_return_if_fail (IDE_IS_SEARCH_RESULT (self));
-  g_return_if_fail (score >= 0.0);
-  g_return_if_fail (score <= 1.0);
 
-  priv->score = score;
+  if (priv->score != score)
+    {
+      priv->score = score;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_SCORE]);
+    }
 }
 
 gint
-ide_search_result_compare (const IdeSearchResult *a,
-                           const IdeSearchResult *b)
-{
-  gfloat fa;
-  gfloat fb;
-
-  g_return_val_if_fail (IDE_IS_SEARCH_RESULT ((IdeSearchResult*)a), 0);
-  g_return_val_if_fail (IDE_IS_SEARCH_RESULT ((IdeSearchResult*)b), 0);
-
-  fa = ide_search_result_get_score ((IdeSearchResult *)a);
-  fb = ide_search_result_get_score ((IdeSearchResult *)b);
-
-  if (fa < fb)
-    return -1;
-  else if (fa > fb)
-    return 1;
-  else
-    return 0;
-}
-
-static void
-ide_search_result_finalize (GObject *object)
+ide_search_result_get_priority (IdeSearchResult *self)
 {
-  IdeSearchResult *self = (IdeSearchResult *)object;
   IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
 
-  g_clear_object (&priv->provider);
-  g_clear_pointer (&priv->title, g_free);
-  g_clear_pointer (&priv->subtitle, g_free);
+  g_return_val_if_fail (IDE_IS_SEARCH_RESULT (self), 0.0);
 
-  G_OBJECT_CLASS (ide_search_result_parent_class)->finalize (object);
+  return priv->priority;
 }
 
-static void
-ide_search_result_get_property (GObject    *object,
-                                guint       prop_id,
-                                GValue     *value,
-                                GParamSpec *pspec)
+void
+ide_search_result_set_priority (IdeSearchResult *self,
+                                gint             priority)
 {
-  IdeSearchResult *self = IDE_SEARCH_RESULT (object);
-
-  switch (prop_id)
-    {
-    case PROP_PROVIDER:
-      g_value_set_object (value, ide_search_result_get_provider (self));
-      break;
-
-    case PROP_TITLE:
-      g_value_set_string (value, ide_search_result_get_title (self));
-      break;
-
-    case PROP_SUBTITLE:
-      g_value_set_string (value, ide_search_result_get_subtitle (self));
-      break;
-
-    case PROP_SCORE:
-      g_value_set_float (value, ide_search_result_get_score (self));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
-}
+  IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
 
-static void
-ide_search_result_set_property (GObject      *object,
-                                guint         prop_id,
-                                const GValue *value,
-                                GParamSpec   *pspec)
-{
-  IdeSearchResult *self = IDE_SEARCH_RESULT (object);
+  g_return_if_fail (IDE_IS_SEARCH_RESULT (self));
 
-  switch (prop_id)
+  if (priv->priority != priority)
     {
-    case PROP_PROVIDER:
-      ide_search_result_set_provider (self, g_value_get_object (value));
-      break;
-
-    case PROP_TITLE:
-      ide_search_result_set_title (self, g_value_get_string (value));
-      break;
-
-    case PROP_SUBTITLE:
-      ide_search_result_set_subtitle (self, g_value_get_string (value));
-      break;
-
-    case PROP_SCORE:
-      ide_search_result_set_score (self, g_value_get_float (value));
-      break;
-
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      priv->priority = priority;
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PRIORITY]);
     }
 }
-
-static void
-ide_search_result_class_init (IdeSearchResultClass *klass)
-{
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
-  object_class->finalize = ide_search_result_finalize;
-  object_class->get_property = ide_search_result_get_property;
-  object_class->set_property = ide_search_result_set_property;
-
-  properties [PROP_PROVIDER] =
-    g_param_spec_object ("provider",
-                         "Provider",
-                         "The Search Provider",
-                         IDE_TYPE_SEARCH_PROVIDER,
-                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-
-  properties [PROP_TITLE] =
-    g_param_spec_string ("title",
-                         "Title",
-                         "The title of the search result.",
-                         NULL,
-                         (G_PARAM_READWRITE |
-                          G_PARAM_CONSTRUCT_ONLY |
-                          G_PARAM_STATIC_STRINGS));
-
-  properties [PROP_SUBTITLE] =
-    g_param_spec_string ("subtitle",
-                         "Subtitle",
-                         "The subtitle of the search result.",
-                         NULL,
-                         (G_PARAM_READWRITE |
-                          G_PARAM_CONSTRUCT_ONLY |
-                          G_PARAM_STATIC_STRINGS));
-
-  properties [PROP_SCORE] =
-    g_param_spec_float ("score",
-                        "Score",
-                        "The score of the search result.",
-                        0.0,
-                        1.0,
-                        0.0,
-                        (G_PARAM_READWRITE |
-                         G_PARAM_CONSTRUCT_ONLY |
-                         G_PARAM_STATIC_STRINGS));
-
-  g_object_class_install_properties (object_class, LAST_PROP, properties);
-}
-
-static void
-ide_search_result_init (IdeSearchResult *self)
-{
-}
diff --git a/libide/search/ide-search-result.h b/libide/search/ide-search-result.h
index 7a69e3b..99182f0 100644
--- a/libide/search/ide-search-result.h
+++ b/libide/search/ide-search-result.h
@@ -1,6 +1,6 @@
 /* ide-search-result.h
  *
- * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ * Copyright (C) 2017 Christian Hergert <chergert redhat com>
  *
  * 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
@@ -19,34 +19,34 @@
 #ifndef IDE_SEARCH_RESULT_H
 #define IDE_SEARCH_RESULT_H
 
-#include "ide-object.h"
-
-#include "ide-search-provider.h"
+#include <gio/gio.h>
+#include <dazzle.h>
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_SEARCH_RESULT (ide_search_result_get_type())
 
-G_DECLARE_DERIVABLE_TYPE (IdeSearchResult, ide_search_result, IDE, SEARCH_RESULT, IdeObject)
+G_DECLARE_DERIVABLE_TYPE (IdeSearchResult, ide_search_result, IDE, SEARCH_RESULT, DzlSuggestion)
 
 struct _IdeSearchResultClass
 {
-  IdeObjectClass parent;
+  DzlSuggestionClass parent_class;
 
-  void (*activate) (IdeSearchResult *result);
+  gpointer _reserved1;
+  gpointer _reserved2;
+  gpointer _reserved3;
+  gpointer _reserved4;
 };
 
-IdeSearchResult   *ide_search_result_new          (IdeSearchProvider     *provider,
-                                                   const gchar           *title,
-                                                   const gchar           *subtitle,
-                                                   gfloat                 score);
-IdeSearchProvider *ide_search_result_get_provider (IdeSearchResult       *result);
-gfloat             ide_search_result_get_score    (IdeSearchResult       *result);
-const gchar       *ide_search_result_get_title    (IdeSearchResult       *result);
-const gchar       *ide_search_result_get_subtitle (IdeSearchResult       *result);
-gint               ide_search_result_compare      (const IdeSearchResult *a,
-                                                   const IdeSearchResult *b);
-void               ide_search_result_activate     (IdeSearchResult       *result);
+IdeSearchResult *ide_search_result_new          (void);
+gint             ide_search_result_compare      (gconstpointer          a,
+                                                 gconstpointer          b);
+gint             ide_search_result_get_priority (IdeSearchResult       *self);
+void             ide_search_result_set_priority (IdeSearchResult       *self,
+                                                 gint                   priority);
+gfloat           ide_search_result_get_score    (IdeSearchResult       *self);
+void             ide_search_result_set_score    (IdeSearchResult       *self,
+                                                 gfloat                 score);
 
 G_END_DECLS
 
diff --git a/libide/workbench/ide-workbench-header-bar.c b/libide/workbench/ide-workbench-header-bar.c
index 7e9e64d..4a62703 100644
--- a/libide/workbench/ide-workbench-header-bar.c
+++ b/libide/workbench/ide-workbench-header-bar.c
@@ -21,7 +21,7 @@
 #include <dazzle.h>
 
 #include "application/ide-application.h"
-#include "search/ide-omni-search-entry.h"
+#include "search/ide-search-entry.h"
 #include "util/ide-gtk.h"
 #include "workbench/ide-perspective.h"
 #include "workbench/ide-workbench.h"
@@ -29,11 +29,11 @@
 
 typedef struct
 {
-  GtkMenuButton      *menu_button;
-  DzlPriorityBox     *right_box;
-  DzlPriorityBox     *left_box;
-  IdeOmniBar         *omni_bar;
-  IdeOmniSearchEntry *search_entry;
+  GtkMenuButton   *menu_button;
+  DzlPriorityBox  *right_box;
+  DzlPriorityBox  *left_box;
+  IdeOmniBar      *omni_bar;
+  IdeSearchEntry  *search_entry;
 } IdeWorkbenchHeaderBarPrivate;
 
 static void buildable_iface_init (GtkBuildableIface *iface);
@@ -59,6 +59,8 @@ ide_workbench_header_bar_class_init (IdeWorkbenchHeaderBarClass *klass)
   gtk_widget_class_bind_template_child_private (widget_class, IdeWorkbenchHeaderBar, omni_bar);
   gtk_widget_class_bind_template_child_private (widget_class, IdeWorkbenchHeaderBar, right_box);
   gtk_widget_class_bind_template_child_private (widget_class, IdeWorkbenchHeaderBar, search_entry);
+
+  g_type_ensure (IDE_TYPE_SEARCH_ENTRY);
 }
 
 static void
diff --git a/libide/workbench/ide-workbench-header-bar.ui b/libide/workbench/ide-workbench-header-bar.ui
index 1f83171..8705244 100644
--- a/libide/workbench/ide-workbench-header-bar.ui
+++ b/libide/workbench/ide-workbench-header-bar.ui
@@ -68,7 +68,7 @@
           </packing>
         </child>
         <child>
-          <object class="IdeOmniSearchEntry" id="search_entry">
+          <object class="IdeSearchEntry" id="search_entry">
             <property name="max-width-chars">30</property>
             <property name="placeholder-text" translatable="yes">Press Ctrl+. to search</property>
             <property name="visible">true</property>
diff --git a/plugins/file-search/gb-file-search-index.c b/plugins/file-search/gb-file-search-index.c
index 44d5384..ccf52db 100644
--- a/plugins/file-search/gb-file-search-index.c
+++ b/plugins/file-search/gb-file-search-index.c
@@ -261,6 +261,8 @@ gb_file_search_index_build_async (GbFileSearchIndex   *self,
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, gb_file_search_index_build_async);
+  g_task_set_priority (task, G_PRIORITY_LOW);
 
   if (self->root_directory == NULL)
     {
@@ -289,31 +291,24 @@ gb_file_search_index_build_finish (GbFileSearchIndex  *self,
   return g_task_propagate_boolean (task, error);
 }
 
-void
+GPtrArray *
 gb_file_search_index_populate (GbFileSearchIndex *self,
-                               IdeSearchContext  *context,
-                               IdeSearchProvider *provider,
-                               const gchar       *query)
+                               const gchar       *query,
+                               gsize              max_results)
 {
-  g_autoptr(GArray) ar = NULL;
   g_auto(IdeSearchReducer) reducer = { 0 };
   g_autoptr(GString) delimited = NULL;
+  g_autoptr(GArray) ar = NULL;
   const gchar *iter = query;
-  IdeContext *icontext;
-  gsize max_matches;
   gsize i;
 
-  g_return_if_fail (GB_IS_FILE_SEARCH_INDEX (self));
-  g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
-  g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
-  g_return_if_fail (query != NULL);
+  g_return_val_if_fail (GB_IS_FILE_SEARCH_INDEX (self), NULL);
+  g_return_val_if_fail (query != NULL, NULL);
 
   if (self->fuzzy == NULL)
-    return;
+    return g_ptr_array_new_with_free_func (g_object_unref);
 
-  icontext = ide_object_get_context (IDE_OBJECT (provider));
-  max_matches = ide_search_context_get_max_results (context);
-  ide_search_reducer_init (&reducer, context, provider, max_matches);
+  ide_search_reducer_init (&reducer, max_results);
 
   delimited = g_string_new (NULL);
 
@@ -325,13 +320,11 @@ gb_file_search_index_populate (GbFileSearchIndex *self,
         g_string_append_unichar (delimited, ch);
     }
 
-  ar = dzl_fuzzy_mutable_index_match (self->fuzzy, delimited->str, max_matches);
+  ar = dzl_fuzzy_mutable_index_match (self->fuzzy, delimited->str, max_results);
 
   for (i = 0; i < ar->len; i++)
     {
-      const DzlFuzzyMutableIndexMatch *match;
-
-      match = &g_array_index (ar, DzlFuzzyMutableIndexMatch, i);
+      const DzlFuzzyMutableIndexMatch *match = &g_array_index (ar, DzlFuzzyMutableIndexMatch, i);
 
       if (ide_search_reducer_accepts (&reducer, match->score))
         {
@@ -339,16 +332,17 @@ gb_file_search_index_populate (GbFileSearchIndex *self,
           g_autofree gchar *markup = NULL;
 
           markup = dzl_fuzzy_highlight (match->key, delimited->str, FALSE);
-          result = g_object_new (GB_TYPE_FILE_SEARCH_RESULT,
-                                 "context", icontext,
-                                 "provider", provider,
+
+          result = g_object_new (IDE_TYPE_SEARCH_RESULT,
                                  "score", match->score,
                                  "title", markup,
-                                 "path", match->key,
+                                 //"path", match->key,
                                  NULL);
-          ide_search_reducer_push (&reducer, IDE_SEARCH_RESULT (result));
+          ide_search_reducer_take (&reducer, g_steal_pointer (&result));
         }
     }
+
+  return ide_search_reducer_free (&reducer, FALSE);
 }
 
 gboolean
diff --git a/plugins/file-search/gb-file-search-index.h b/plugins/file-search/gb-file-search-index.h
index 6f1924a..5384b18 100644
--- a/plugins/file-search/gb-file-search-index.h
+++ b/plugins/file-search/gb-file-search-index.h
@@ -27,23 +27,22 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GbFileSearchIndex, gb_file_search_index, GB, FILE_SEARCH_INDEX, IdeObject)
 
-void     gb_file_search_index_populate     (GbFileSearchIndex    *self,
-                                            IdeSearchContext     *context,
-                                            IdeSearchProvider    *provider,
-                                            const gchar          *query);
-void     gb_file_search_index_build_async  (GbFileSearchIndex    *self,
-                                            GCancellable         *cancellable,
-                                            GAsyncReadyCallback   callback,
-                                            gpointer              user_data);
-gboolean gb_file_search_index_build_finish (GbFileSearchIndex    *self,
-                                            GAsyncResult         *result,
-                                            GError              **error);
-gboolean gb_file_search_index_contains     (GbFileSearchIndex    *self,
-                                            const gchar          *relative_path);
-void     gb_file_search_index_insert       (GbFileSearchIndex    *self,
-                                            const gchar          *relative_path);
-void     gb_file_search_index_remove       (GbFileSearchIndex    *self,
-                                            const gchar          *relative_path);
+GPtrArray *gb_file_search_index_populate     (GbFileSearchIndex    *self,
+                                              const gchar          *query,
+                                              gsize                 max_results);
+void       gb_file_search_index_build_async  (GbFileSearchIndex    *self,
+                                              GCancellable         *cancellable,
+                                              GAsyncReadyCallback   callback,
+                                              gpointer              user_data);
+gboolean   gb_file_search_index_build_finish (GbFileSearchIndex    *self,
+                                              GAsyncResult         *result,
+                                              GError              **error);
+gboolean   gb_file_search_index_contains     (GbFileSearchIndex    *self,
+                                              const gchar          *relative_path);
+void       gb_file_search_index_insert       (GbFileSearchIndex    *self,
+                                              const gchar          *relative_path);
+void       gb_file_search_index_remove       (GbFileSearchIndex    *self,
+                                              const gchar          *relative_path);
 
 G_END_DECLS
 
diff --git a/plugins/file-search/gb-file-search-provider.c b/plugins/file-search/gb-file-search-provider.c
index 7cc142c..e4b5659 100644
--- a/plugins/file-search/gb-file-search-provider.c
+++ b/plugins/file-search/gb-file-search-provider.c
@@ -35,33 +35,45 @@ G_DEFINE_DYNAMIC_TYPE_EXTENDED (GbFileSearchProvider,
                                 gb_file_search_provider,
                                 IDE_TYPE_OBJECT,
                                 0,
-                                G_IMPLEMENT_INTERFACE (IDE_TYPE_SEARCH_PROVIDER,
-                                                       search_provider_iface_init))
-
-static const gchar *
-gb_file_search_provider_get_verb (IdeSearchProvider *provider)
-{
-  return _("Switch To");
-}
+                                G_IMPLEMENT_INTERFACE (IDE_TYPE_SEARCH_PROVIDER, search_provider_iface_init))
 
 static void
-gb_file_search_provider_populate (IdeSearchProvider *provider,
-                                  IdeSearchContext  *context,
-                                  const gchar       *search_terms,
-                                  gsize              max_results,
-                                  GCancellable      *cancellable)
+gb_file_search_provider_search_async (IdeSearchProvider   *provider,
+                                      const gchar         *search_terms,
+                                      guint                max_results,
+                                      GCancellable        *cancellable,
+                                      GAsyncReadyCallback  callback,
+                                      gpointer             user_data)
 {
   GbFileSearchProvider *self = (GbFileSearchProvider *)provider;
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GPtrArray) results = NULL;
 
-  g_assert (IDE_IS_SEARCH_PROVIDER (provider));
-  g_assert (IDE_IS_SEARCH_CONTEXT (context));
+  g_assert (GB_IS_FILE_SEARCH_PROVIDER (self));
   g_assert (search_terms != NULL);
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, gb_file_search_provider_search_async);
+  g_task_set_priority (task, G_PRIORITY_LOW);
+
   if (self->index != NULL)
-    gb_file_search_index_populate (self->index, context, provider, search_terms);
+    results = gb_file_search_index_populate (self->index, search_terms, max_results);
+  else
+    results = g_ptr_array_new_with_free_func (g_object_unref);
+
+  g_task_return_pointer (task, g_steal_pointer (&results), (GDestroyNotify)g_ptr_array_unref);
+}
+
+static GPtrArray *
+gb_file_search_provider_search_finish (IdeSearchProvider  *provider,
+                                       GAsyncResult       *result,
+                                       GError            **error)
+{
+  g_assert (GB_IS_FILE_SEARCH_PROVIDER (provider));
+  g_assert (G_IS_TASK (result));
 
-  ide_search_context_provider_completed (context, provider);
+  return g_task_propagate_pointer (G_TASK (result), error);
 }
 
 static void
@@ -167,20 +179,7 @@ gb_file_search_provider_build_cb (GObject      *object,
   g_set_object (&self->index, index);
 }
 
-static GtkWidget *
-gb_file_search_provider_create_row (IdeSearchProvider *provider,
-                                    IdeSearchResult   *result)
-{
-  g_assert (IDE_IS_SEARCH_PROVIDER (provider));
-  g_assert (IDE_IS_SEARCH_RESULT (result));
-
-  return g_object_new (IDE_TYPE_OMNI_SEARCH_ROW,
-                       "icon-name", "text-x-generic-symbolic",
-                       "result", result,
-                       "visible", TRUE,
-                       NULL);
-}
-
+#if 0
 static void
 gb_file_search_provider_activate (IdeSearchProvider *provider,
                                   GtkWidget         *row,
@@ -218,12 +217,7 @@ gb_file_search_provider_activate (IdeSearchProvider *provider,
                                       NULL);
     }
 }
-
-static gint
-gb_file_search_provider_get_priority (IdeSearchProvider *provider)
-{
-  return 0;
-}
+#endif
 
 static void
 gb_file_search_provider_vcs_changed_cb (GbFileSearchProvider *self,
@@ -342,11 +336,8 @@ gb_file_search_provider_init (GbFileSearchProvider *self)
 static void
 search_provider_iface_init (IdeSearchProviderInterface *iface)
 {
-  iface->populate = gb_file_search_provider_populate;
-  iface->get_verb = gb_file_search_provider_get_verb;
-  iface->create_row = gb_file_search_provider_create_row;
-  iface->activate = gb_file_search_provider_activate;
-  iface->get_priority = gb_file_search_provider_get_priority;
+  iface->search_async = gb_file_search_provider_search_async;
+  iface->search_finish = gb_file_search_provider_search_finish;
 }
 
 void


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