[gnome-builder/wip/plugins] search: refactor search into PeasExtension



commit 79a520bd2a5e4446b897b6232b924bf1b95be486
Author: Christian Hergert <christian hergert me>
Date:   Fri Jun 12 01:14:52 2015 -0700

    search: refactor search into PeasExtension
    
    This moves the devhelp search provider to be implemented as a peas
    extension. We had to change the interfaces a bit to be able to do this,
    but should be much easier to bridge the libide<->ui stuff going forward.
    
    We still need to update the git index as well, but we don't want that to
    be git based going forward (it needs to crawl the filesystem) so that it
    works with regular directory trees too.

 data/ui/gb-search-display-row.ui                   |   59 ++++++-----
 libide/Makefile.am                                 |    5 -
 libide/git/ide-git-search-index.c                  |    1 +
 libide/git/ide-git-search-provider.c               |   25 +++--
 libide/git/ide-git-search-provider.h               |    5 +-
 libide/ide-context.c                               |   58 ----------
 libide/ide-search-engine.c                         |   83 ++++++---------
 libide/ide-search-engine.h                         |    8 +-
 libide/ide-search-provider.c                       |  114 +++++++++++++++-----
 libide/ide-search-provider.h                       |   22 +++--
 libide/ide-search-result.c                         |   72 +++++++++++--
 libide/ide-search-result.h                         |   23 +++--
 libide/ide.c                                       |    9 +--
 libide/ide.h                                       |    2 +-
 plugins/devhelp/Makefile.am                        |    6 +
 plugins/devhelp/gb-devhelp-panel.c                 |   21 +++-
 plugins/devhelp/gb-devhelp-panel.h                 |    3 +
 .../devhelp/gb-devhelp-plugin.c                    |   25 ++---
 .../devhelp/ide-devhelp-search-provider.c          |   85 ++++++++++++---
 .../devhelp/ide-devhelp-search-provider.h          |   13 +--
 .../devhelp/ide-devhelp-search-result.c            |    0
 .../devhelp/ide-devhelp-search-result.h            |    0
 src/Makefile.am                                    |    1 +
 src/search/gb-search-box.c                         |    5 +-
 src/search/gb-search-display-group.c               |   75 ++++++-------
 src/search/gb-search-display-row.c                 |    2 +-
 src/search/gb-search-display-row.h                 |    4 +-
 src/search/gb-search-display.c                     |    4 +-
 src/util/gb-widget.c                               |   46 ++++++++
 src/util/gb-widget.h                               |    2 +
 tools/ide-search.c                                 |    2 +-
 31 files changed, 471 insertions(+), 309 deletions(-)
---
diff --git a/data/ui/gb-search-display-row.ui b/data/ui/gb-search-display-row.ui
index fdd0735..2a2a240 100644
--- a/data/ui/gb-search-display-row.ui
+++ b/data/ui/gb-search-display-row.ui
@@ -1,44 +1,49 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <interface>
-  <!-- interface-requires gtk+ 3.8 -->
-  <template class="GbSearchDisplayRow" parent="GtkBox">
-    <property name="orientation">horizontal</property>
-    <property name="spacing">6</property>
+  <!-- interface-requires gtk+ 3.16 -->
+  <template class="GbSearchDisplayRow" parent="GtkListBoxRow">
     <child>
       <object class="GtkBox">
-        <property name="hexpand">True</property>
-        <property name="orientation">vertical</property>
-        <property name="margin_start">6</property>
-        <property name="margin_top">3</property>
-        <property name="margin_bottom">3</property>
-        <property name="visible">True</property>
+        <property name="visible">true</property>
+        <property name="orientation">horizontal</property>
+        <property name="spacing">6</property>
         <child>
-          <object class="GtkLabel" id="title">
+          <object class="GtkBox">
+            <property name="hexpand">True</property>
+            <property name="orientation">vertical</property>
+            <property name="margin_start">6</property>
+            <property name="margin_top">3</property>
+            <property name="margin_bottom">3</property>
             <property name="visible">True</property>
-            <property name="xalign">0.0</property>
+            <child>
+              <object class="GtkLabel" id="title">
+                <property name="visible">True</property>
+                <property name="xalign">0.0</property>
+              </object>
+            </child>
+            <child>
+              <object class="GtkLabel" id="subtitle">
+                <property name="visible">False</property>
+                <property name="xalign">0.0</property>
+                <style>
+                  <class name="dim-label"/>
+                </style>
+              </object>
+            </child>
           </object>
         </child>
         <child>
-          <object class="GtkLabel" id="subtitle">
-            <property name="visible">False</property>
-            <property name="xalign">0.0</property>
+          <object class="GtkProgressBar" id="progress">
+            <property name="inverted">True</property>
+            <property name="visible">True</property>
+            <property name="valign">center</property>
+            <property name="margin_end">6</property>
             <style>
-              <class name="dim-label"/>
+              <class name="osd"/>
             </style>
           </object>
         </child>
       </object>
     </child>
-    <child>
-      <object class="GtkProgressBar" id="progress">
-        <property name="inverted">True</property>
-        <property name="visible">True</property>
-        <property name="valign">center</property>
-        <property name="margin_end">6</property>
-        <style>
-          <class name="osd"/>
-        </style>
-      </object>
-    </child>
   </template>
 </interface>
diff --git a/libide/Makefile.am b/libide/Makefile.am
index f6141b1..ab0137e 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -9,8 +9,6 @@ pkglib_LTLIBRARIES = libide-1.0.la
 libide_1_0_la_public_sources = \
        c/ide-c-language.c \
        c/ide-c-language.h \
-       devhelp/ide-devhelp-search-result.c \
-       devhelp/ide-devhelp-search-result.h \
        directory/ide-directory-build-system.c \
        directory/ide-directory-build-system.h \
        directory/ide-directory-vcs.c \
@@ -235,8 +233,6 @@ libide_1_0_la_SOURCES = \
        ctags/ide-ctags-index.h \
        ctags/ide-ctags-service.c \
        ctags/ide-ctags-service.h \
-       devhelp/ide-devhelp-search-provider.c \
-       devhelp/ide-devhelp-search-provider.h \
        editorconfig/editorconfig-glib.c \
        editorconfig/editorconfig-glib.h \
        editorconfig/ide-editorconfig-file-settings.c \
@@ -346,7 +342,6 @@ libide_1_0_la_includes = \
        -I$(srcdir)/c \
        -I$(srcdir)/clang \
        -I$(srcdir)/ctags \
-       -I$(srcdir)/devhelp \
        -I$(srcdir)/directory \
        -I$(srcdir)/doap \
        -I$(srcdir)/editorconfig \
diff --git a/libide/git/ide-git-search-index.c b/libide/git/ide-git-search-index.c
index 005fdc7..62b148c 100644
--- a/libide/git/ide-git-search-index.c
+++ b/libide/git/ide-git-search-index.c
@@ -236,6 +236,7 @@ ide_git_search_index_populate (IdeGitSearchIndex *self,
 
           result = g_object_new (IDE_TYPE_GIT_SEARCH_RESULT,
                                  "context", context,
+                                 "provider", self,
                                  "title", markup,
                                  "subtitle", str->str,
                                  "score", match->score,
diff --git a/libide/git/ide-git-search-provider.c b/libide/git/ide-git-search-provider.c
index 729dfea..dc481f9 100644
--- a/libide/git/ide-git-search-provider.c
+++ b/libide/git/ide-git-search-provider.c
@@ -26,8 +26,7 @@
 
 struct _IdeGitSearchProvider
 {
-  IdeSearchProvider  parent_instance;
-
+  IdeObject          parent_instance;
   IdeGitSearchIndex *index;
 };
 
@@ -38,8 +37,13 @@ typedef struct
   gsize             max_results;
 } PopulateState;
 
-G_DEFINE_TYPE (IdeGitSearchProvider, ide_git_search_provider,
-               IDE_TYPE_SEARCH_PROVIDER)
+static void search_provider_iface_init (IdeSearchProviderInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (IdeGitSearchProvider,
+                         ide_git_search_provider,
+                         IDE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_SEARCH_PROVIDER,
+                                                search_provider_iface_init))
 
 static void
 ide_git_search_provider_get_index_cb (GObject      *object,
@@ -176,7 +180,7 @@ ide_git_search_provider_populate (IdeSearchProvider *provider,
 
   g_return_if_fail (IDE_IS_GIT_SEARCH_PROVIDER (self));
   g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
-  g_return_if_fail (search_terms);
+  g_return_if_fail (search_terms != NULL);
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   state = g_slice_new0 (PopulateState);
@@ -210,15 +214,18 @@ static void
 ide_git_search_provider_class_init (IdeGitSearchProviderClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  IdeSearchProviderClass *provider_class = IDE_SEARCH_PROVIDER_CLASS (klass);
 
   object_class->finalize = ide_git_search_provider_finalize;
-
-  provider_class->get_verb = ide_git_search_provider_get_verb;
-  provider_class->populate = ide_git_search_provider_populate;
 }
 
 static void
 ide_git_search_provider_init (IdeGitSearchProvider *self)
 {
 }
+
+static void
+search_provider_iface_init (IdeSearchProviderInterface *iface)
+{
+  iface->get_verb = ide_git_search_provider_get_verb;
+  iface->populate = ide_git_search_provider_populate;
+}
diff --git a/libide/git/ide-git-search-provider.h b/libide/git/ide-git-search-provider.h
index 3fa5343..7fd04eb 100644
--- a/libide/git/ide-git-search-provider.h
+++ b/libide/git/ide-git-search-provider.h
@@ -25,10 +25,9 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_GIT_SEARCH_PROVIDER (ide_git_search_provider_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeGitSearchProvider,
-                      ide_git_search_provider,
+G_DECLARE_FINAL_TYPE (IdeGitSearchProvider, ide_git_search_provider,
                       IDE, GIT_SEARCH_PROVIDER,
-                      IdeSearchProvider)
+                      IdeObject)
 
 G_END_DECLS
 
diff --git a/libide/ide-context.c b/libide/ide-context.c
index 2920441..6190ba6 100644
--- a/libide/ide-context.c
+++ b/libide/ide-context.c
@@ -1261,63 +1261,6 @@ ide_context_init_services (gpointer             source_object,
 }
 
 static void
-ide_context_init_search_engine (gpointer             source_object,
-                                GCancellable        *cancellable,
-                                GAsyncReadyCallback  callback,
-                                gpointer             user_data)
-{
-  g_autoptr(GTask) task = NULL;
-  IdeContext *self = source_object;
-  GIOExtensionPoint *point;
-  const GList *iter;
-  const GList *list;
-
-  g_return_if_fail (IDE_IS_CONTEXT (self));
-  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
-
-  task = g_task_new (self, cancellable, callback, user_data);
-
-  point = g_io_extension_point_lookup (IDE_SEARCH_PROVIDER_EXTENSION_POINT);
-
-  if (!point)
-    {
-      g_task_return_new_error (task,
-                               G_IO_ERROR,
-                               G_IO_ERROR_NOT_SUPPORTED,
-                               _("Missing extension point for %s"),
-                               IDE_SEARCH_PROVIDER_EXTENSION_POINT);
-      return;
-    }
-
-  list = g_io_extension_point_get_extensions (point);
-
-  for (iter = list; iter; iter = iter->next)
-    {
-      GIOExtension *extension = iter->data;
-      IdeSearchProvider *provider;
-      GType gtype;
-
-      gtype = g_io_extension_get_type (extension);
-
-      if (!g_type_is_a (gtype, IDE_TYPE_SEARCH_PROVIDER))
-        {
-          g_task_return_new_error (task,
-                                   G_IO_ERROR,
-                                   G_IO_ERROR_INVALID_DATA,
-                                   _("%s is not an IdeSearchProvider."),
-                                   g_type_name (gtype));
-          return;
-        }
-
-      provider = g_object_new (gtype, "context", self, NULL);
-      ide_search_engine_add_provider (self->search_engine, provider);
-      g_object_unref (provider);
-    }
-
-  g_task_return_boolean (task, TRUE);
-}
-
-static void
 ide_context_init_add_recent (gpointer             source_object,
                              GCancellable        *cancellable,
                              GAsyncReadyCallback  callback,
@@ -1429,7 +1372,6 @@ ide_context_init_async (GAsyncInitable      *initable,
                         ide_context_init_files,
                         ide_context_init_project_name,
                         ide_context_init_back_forward_list,
-                        ide_context_init_search_engine,
                         ide_context_init_snippets,
                         ide_context_init_scripts,
                         ide_context_init_unsaved_files,
diff --git a/libide/ide-search-engine.c b/libide/ide-search-engine.c
index 336aff0..c071b3b 100644
--- a/libide/ide-search-engine.c
+++ b/libide/ide-search-engine.c
@@ -17,6 +17,7 @@
  */
 
 #include <glib/gi18n.h>
+#include <libpeas/peas.h>
 
 #include "ide-internal.h"
 #include "ide-search-context.h"
@@ -26,23 +27,29 @@
 
 struct _IdeSearchEngine
 {
-  IdeObject  parent_instance;
-  GList     *providers;
+  IdeObject         parent_instance;
+
+  PeasExtensionSet *extensions;
 };
 
 G_DEFINE_TYPE (IdeSearchEngine, ide_search_engine, IDE_TYPE_OBJECT)
 
-enum {
-  PROVIDER_ADDED,
-  LAST_SIGNAL
-};
+static void
+add_provider_to_context (PeasExtensionSet *extensions,
+                         PeasPluginInfo   *plugin_info,
+                         PeasExtension    *extension,
+                         IdeSearchContext *search_context)
+{
+  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));
 
-static guint gSignals [LAST_SIGNAL];
+  _ide_search_context_add_provider (search_context, IDE_SEARCH_PROVIDER (extension), 0);
+}
 
 /**
  * ide_search_engine_search:
- * @providers: (allow-none) (element-type IdeSearchProvider*): Optional list
- *   of specific providers to use when searching.
  * @search_terms: The search terms.
  *
  * Begins a query against the requested search providers.
@@ -54,67 +61,48 @@ static guint gSignals [LAST_SIGNAL];
  */
 IdeSearchContext *
 ide_search_engine_search (IdeSearchEngine *self,
-                          const GList     *providers,
                           const gchar     *search_terms)
 {
   IdeSearchContext *search_context;
   IdeContext *context;
-  const GList *iter;
 
   g_return_val_if_fail (IDE_IS_SEARCH_ENGINE (self), NULL);
   g_return_val_if_fail (search_terms, NULL);
 
-  if (!providers)
-    providers = self->providers;
-
   context = ide_object_get_context (IDE_OBJECT (self));
   search_context = g_object_new (IDE_TYPE_SEARCH_CONTEXT,
                                  "context", context,
                                  NULL);
 
-  for (iter = providers; iter; iter = iter->next)
-    _ide_search_context_add_provider (search_context, iter->data, 0);
+  peas_extension_set_foreach (self->extensions,
+                              (PeasExtensionSetForeachFunc)add_provider_to_context,
+                              search_context);
 
   return search_context;
 }
 
-/**
- * ide_search_engine_get_providers:
- *
- * Returns the list of registered search providers.
- *
- * Returns: (transfer none) (element-type IdeSearchProvider*): A #GList of
- *   #IdeSearchProvider.
- */
-GList *
-ide_search_engine_get_providers (IdeSearchEngine *self)
+static void
+ide_search_engine_constructed (GObject *object)
 {
-  g_return_val_if_fail (IDE_IS_SEARCH_ENGINE (self), NULL);
+  IdeSearchEngine *self = (IdeSearchEngine *)object;
+  IdeContext *context;
 
-  return self->providers;
-}
+  context = ide_object_get_context (IDE_OBJECT (self));
 
-void
-ide_search_engine_add_provider (IdeSearchEngine   *self,
-                                IdeSearchProvider *provider)
-{
-  g_return_if_fail (IDE_IS_SEARCH_ENGINE (self));
-  g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
+  self->extensions = peas_extension_set_new (peas_engine_get_default (),
+                                             IDE_TYPE_SEARCH_PROVIDER,
+                                             "context", context,
+                                             NULL);
 
-  self->providers = g_list_append (self->providers, g_object_ref (provider));
-  g_signal_emit (self, gSignals [PROVIDER_ADDED], 0, provider);
+  G_OBJECT_CLASS (ide_search_engine_parent_class)->constructed (object);
 }
 
 static void
 ide_search_engine_dispose (GObject *object)
 {
   IdeSearchEngine *self = (IdeSearchEngine *)object;
-  GList *copy;
 
-  copy = self->providers;
-  self->providers = NULL;
-  g_list_foreach (copy, (GFunc)g_object_unref, NULL);
-  g_list_free (copy);
+  g_clear_object (&self->extensions);
 
   G_OBJECT_CLASS (ide_search_engine_parent_class)->dispose (object);
 }
@@ -124,17 +112,8 @@ 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;
-
-  gSignals [PROVIDER_ADDED] =
-    g_signal_new ("provider-added",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_LAST,
-                  0,
-                  NULL, NULL, NULL,
-                  G_TYPE_NONE,
-                  1,
-                  IDE_TYPE_SEARCH_PROVIDER);
 }
 
 static void
diff --git a/libide/ide-search-engine.h b/libide/ide-search-engine.h
index f0aaedc..38dab7a 100644
--- a/libide/ide-search-engine.h
+++ b/libide/ide-search-engine.h
@@ -27,12 +27,8 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeSearchEngine, ide_search_engine, IDE, SEARCH_ENGINE, IdeObject)
 
-GList            *ide_search_engine_get_providers (IdeSearchEngine   *self);
-void              ide_search_engine_add_provider  (IdeSearchEngine   *self,
-                                                   IdeSearchProvider *provider);
-IdeSearchContext *ide_search_engine_search        (IdeSearchEngine   *self,
-                                                   const GList       *providers,
-                                                   const gchar       *search_terms);
+IdeSearchContext *ide_search_engine_search (IdeSearchEngine   *self,
+                                            const gchar       *search_terms);
 
 G_END_DECLS
 
diff --git a/libide/ide-search-provider.c b/libide/ide-search-provider.c
index 084ae1f..47b5298 100644
--- a/libide/ide-search-provider.c
+++ b/libide/ide-search-provider.c
@@ -16,19 +16,62 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <glib/gi18n.h>
+
+#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)
+{
+  return "";
+}
+
+static gint
+ide_search_provider_real_get_priority (IdeSearchProvider *self)
+{
+  return -1;
+}
 
-G_DEFINE_ABSTRACT_TYPE (IdeSearchProvider, ide_search_provider, IDE_TYPE_OBJECT)
+static gunichar
+ide_search_provider_real_get_prefix (IdeSearchProvider *self)
+{
+  return '\0';
+}
+
+static GtkWidget *
+ide_search_provider_real_create_row (IdeSearchProvider *self,
+                                     IdeSearchResult   *result)
+{
+  return NULL;
+}
 
 static void
-ide_search_provider_class_init (IdeSearchProviderClass *klass)
+ide_search_provider_real_activate (IdeSearchProvider *self,
+                                   GtkWidget         *row,
+                                   IdeSearchResult   *result)
 {
 }
 
 static void
-ide_search_provider_init (IdeSearchProvider *self)
+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;
+
+  g_object_interface_install_property (iface,
+                                       g_param_spec_object ("context",
+                                                            _("Context"),
+                                                            _("Context"),
+                                                            IDE_TYPE_CONTEXT,
+                                                            G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | 
G_PARAM_STATIC_STRINGS));
 }
 
 const gchar *
@@ -36,10 +79,7 @@ ide_search_provider_get_verb (IdeSearchProvider *provider)
 {
   g_return_val_if_fail (IDE_IS_SEARCH_PROVIDER (provider), NULL);
 
-  if (IDE_SEARCH_PROVIDER_GET_CLASS (provider)->get_verb)
-    return IDE_SEARCH_PROVIDER_GET_CLASS (provider)->get_verb (provider);
-
-  return NULL;
+  return IDE_SEARCH_PROVIDER_GET_IFACE (provider)->get_verb (provider);
 }
 
 gint
@@ -47,10 +87,7 @@ ide_search_provider_get_priority (IdeSearchProvider *provider)
 {
   g_return_val_if_fail (IDE_IS_SEARCH_PROVIDER (provider), -1);
 
-  if (IDE_SEARCH_PROVIDER_GET_CLASS (provider)->get_priority)
-    return IDE_SEARCH_PROVIDER_GET_CLASS (provider)->get_priority (provider);
-
-  return G_MAXINT;
+  return IDE_SEARCH_PROVIDER_GET_IFACE (provider)->get_priority (provider);
 }
 
 gunichar
@@ -58,10 +95,7 @@ ide_search_provider_get_prefix (IdeSearchProvider *provider)
 {
   g_return_val_if_fail (IDE_IS_SEARCH_PROVIDER (provider), -1);
 
-  if (IDE_SEARCH_PROVIDER_GET_CLASS (provider)->get_prefix)
-    return IDE_SEARCH_PROVIDER_GET_CLASS (provider)->get_prefix (provider);
-
-  return '\0';
+  return IDE_SEARCH_PROVIDER_GET_IFACE (provider)->get_prefix (provider);
 }
 
 void
@@ -73,19 +107,43 @@ ide_search_provider_populate (IdeSearchProvider *provider,
 {
   g_return_if_fail (IDE_IS_SEARCH_PROVIDER (provider));
   g_return_if_fail (IDE_IS_SEARCH_CONTEXT (context));
-  g_return_if_fail (search_terms);
+  g_return_if_fail (search_terms != NULL);
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
-  if (IDE_SEARCH_PROVIDER_GET_CLASS (provider)->populate)
-    {
-      IDE_SEARCH_PROVIDER_GET_CLASS (provider)->populate (provider,
-                                                          context,
-                                                          search_terms,
-                                                          max_results,
-                                                          cancellable);
-      return;
-    }
-
-  g_warning ("%s does not implement populate vfunc",
-             g_type_name (G_TYPE_FROM_INSTANCE (provider)));
+  return IDE_SEARCH_PROVIDER_GET_IFACE (provider)->populate (provider,
+                                                             context,
+                                                             search_terms,
+                                                             max_results,
+                                                             cancellable);
+}
+
+/**
+ * ide_search_provider_create_row:
+ * @provider: A #IdeSearchProvider.
+ * @result: A #IdeSearchResult.
+ *
+ * Create a row to display the search result.
+ *
+ * Returns: (transfer full): A #GtkWidget.
+ */
+GtkWidget *
+ide_search_provider_create_row (IdeSearchProvider *self,
+                                IdeSearchResult   *result)
+{
+  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_val_if_fail (IDE_IS_SEARCH_PROVIDER (self), NULL);
+  g_return_val_if_fail (GTK_IS_WIDGET (row), NULL);
+  g_return_val_if_fail (IDE_IS_SEARCH_RESULT (result), NULL);
+
+  return IDE_SEARCH_PROVIDER_GET_IFACE (self)->activate (self, row, result);
 }
diff --git a/libide/ide-search-provider.h b/libide/ide-search-provider.h
index 3094f49..c6f3448 100644
--- a/libide/ide-search-provider.h
+++ b/libide/ide-search-provider.h
@@ -19,21 +19,19 @@
 #ifndef IDE_SEARCH_PROVIDER_H
 #define IDE_SEARCH_PROVIDER_H
 
+#include <gtk/gtk.h>
+
 #include "ide-object.h"
 
 G_BEGIN_DECLS
 
 #define IDE_TYPE_SEARCH_PROVIDER (ide_search_provider_get_type())
 
-#define IDE_SEARCH_PROVIDER_EXTENSION_POINT \
-  "org.gnome.libide.extensions.search-provider"
-
-G_DECLARE_DERIVABLE_TYPE (IdeSearchProvider, ide_search_provider,
-                          IDE, SEARCH_PROVIDER, IdeObject)
+G_DECLARE_INTERFACE (IdeSearchProvider, ide_search_provider, IDE, SEARCH_PROVIDER, IdeObject)
 
-struct _IdeSearchProviderClass
+struct _IdeSearchProviderInterface
 {
-  IdeObjectClass parent_class;
+  GTypeInterface parent_iface;
 
   gunichar     (*get_prefix)   (IdeSearchProvider *provider);
   gint         (*get_priority) (IdeSearchProvider *provider);
@@ -43,6 +41,11 @@ struct _IdeSearchProviderClass
                                 const gchar       *search_terms,
                                 gsize              max_results,
                                 GCancellable      *cancellable);
+  GtkWidget  *(*create_row)    (IdeSearchProvider *provider,
+                                IdeSearchResult   *result);
+  void        (*activate)      (IdeSearchProvider *provider,
+                                GtkWidget         *row,
+                                IdeSearchResult   *result);
 };
 
 gunichar     ide_search_provider_get_prefix   (IdeSearchProvider *provider);
@@ -53,6 +56,11 @@ void         ide_search_provider_populate     (IdeSearchProvider *provider,
                                                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);
 
 G_END_DECLS
 
diff --git a/libide/ide-search-result.c b/libide/ide-search-result.c
index 00a340f..7d50e55 100644
--- a/libide/ide-search-result.c
+++ b/libide/ide-search-result.c
@@ -23,15 +23,17 @@
 
 typedef struct
 {
-  gchar  *title;
-  gchar  *subtitle;
-  gfloat  score;
+  IdeSearchProvider *provider;
+  gchar             *title;
+  gchar             *subtitle;
+  gfloat             score;
 } IdeSearchResultPrivate;
 
 G_DEFINE_TYPE_WITH_PRIVATE (IdeSearchResult, ide_search_result, IDE_TYPE_OBJECT)
 
 enum {
   PROP_0,
+  PROP_PROVIDER,
   PROP_SCORE,
   PROP_SUBTITLE,
   PROP_TITLE,
@@ -41,25 +43,59 @@ enum {
 static GParamSpec *gParamSpecs [LAST_PROP];
 
 IdeSearchResult *
-ide_search_result_new (IdeContext  *context,
-                       const gchar *title,
-                       const gchar *subtitle,
-                       gfloat       score)
+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_CONTEXT (context), NULL);
+  g_return_val_if_fail (IDE_IS_SEARCH_PROVIDER (provider), NULL);
+
+  context = ide_object_get_context (IDE_OBJECT (provider));
 
   self = g_object_new (IDE_TYPE_SEARCH_RESULT,
                        "context", context,
-                       "title", title,
-                       "subtitle", subtitle,
+                       "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)
+{
+  IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_SEARCH_RESULT (self), NULL);
+
+  return priv->provider;
+}
+
+static void
+ide_search_result_set_provider (IdeSearchResult   *self,
+                                IdeSearchProvider *provider)
+{
+  IdeSearchResultPrivate *priv = ide_search_result_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_SEARCH_RESULT (self));
+  g_return_if_fail (!provider || IDE_IS_SEARCH_PROVIDER (provider));
+
+  g_set_object (&priv->provider, provider);
+}
+
 const gchar *
 ide_search_result_get_title (IdeSearchResult *self)
 {
@@ -160,6 +196,7 @@ ide_search_result_finalize (GObject *object)
   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);
 
@@ -176,6 +213,10 @@ ide_search_result_get_property (GObject    *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;
@@ -203,6 +244,10 @@ ide_search_result_set_property (GObject      *object,
 
   switch (prop_id)
     {
+    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;
@@ -229,6 +274,13 @@ ide_search_result_class_init (IdeSearchResultClass *klass)
   object_class->get_property = ide_search_result_get_property;
   object_class->set_property = ide_search_result_set_property;
 
+  gParamSpecs [PROP_PROVIDER] =
+    g_param_spec_object ("provider",
+                         _("Provider"),
+                         _("The Search Provider"),
+                         IDE_TYPE_SEARCH_PROVIDER,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
   gParamSpecs [PROP_TITLE] =
     g_param_spec_string ("title",
                          _("Title"),
diff --git a/libide/ide-search-result.h b/libide/ide-search-result.h
index d7ca1d0..7a69e3b 100644
--- a/libide/ide-search-result.h
+++ b/libide/ide-search-result.h
@@ -21,6 +21,8 @@
 
 #include "ide-object.h"
 
+#include "ide-search-provider.h"
+
 G_BEGIN_DECLS
 
 #define IDE_TYPE_SEARCH_RESULT (ide_search_result_get_type())
@@ -34,16 +36,17 @@ struct _IdeSearchResultClass
   void (*activate) (IdeSearchResult *result);
 };
 
-IdeSearchResult *ide_search_result_new          (IdeContext            *context,
-                                                 const gchar           *title,
-                                                 const gchar           *subtitle,
-                                                 gfloat                 score);
-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          (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);
 
 G_END_DECLS
 
diff --git a/libide/ide.c b/libide/ide.c
index 0cb3d4e..cbe135a 100644
--- a/libide/ide.c
+++ b/libide/ide.c
@@ -29,7 +29,6 @@
 #include "ide-c-language.h"
 #include "ide-clang-service.h"
 #include "ide-ctags-service.h"
-#include "ide-devhelp-search-provider.h"
 #include "ide-device-provider.h"
 #include "ide-directory-build-system.h"
 #include "ide-directory-vcs.h"
@@ -101,7 +100,6 @@ ide_init_ctor (void)
   g_io_extension_point_register (IDE_LANGUAGE_EXTENSION_POINT);
   g_io_extension_point_register (IDE_PROJECT_MINER_EXTENSION_POINT);
   g_io_extension_point_register (IDE_SCRIPT_EXTENSION_POINT);
-  g_io_extension_point_register (IDE_SEARCH_PROVIDER_EXTENSION_POINT);
   g_io_extension_point_register (IDE_SERVICE_EXTENSION_POINT);
   g_io_extension_point_register (IDE_VCS_EXTENSION_POINT);
 
@@ -168,15 +166,12 @@ ide_init_ctor (void)
                                   IDE_SCRIPT_EXTENSION_POINT".py",
                                   -100);
 
-  g_io_extension_point_implement (IDE_SEARCH_PROVIDER_EXTENSION_POINT,
-                                  IDE_TYPE_DEVHELP_SEARCH_PROVIDER,
-                                  IDE_SEARCH_PROVIDER_EXTENSION_POINT".devhelp",
-                                  -100);
-
+#if 0
   g_io_extension_point_implement (IDE_SEARCH_PROVIDER_EXTENSION_POINT,
                                   IDE_TYPE_GIT_SEARCH_PROVIDER,
                                   IDE_SEARCH_PROVIDER_EXTENSION_POINT".git",
                                   -100);
+#endif
 
   g_io_extension_point_implement (IDE_SERVICE_EXTENSION_POINT,
                                   IDE_TYPE_CLANG_SERVICE,
diff --git a/libide/ide.h b/libide/ide.h
index 398af02..d997ff8 100644
--- a/libide/ide.h
+++ b/libide/ide.h
@@ -71,6 +71,7 @@ G_BEGIN_DECLS
 #include "ide-search-context.h"
 #include "ide-search-engine.h"
 #include "ide-search-provider.h"
+#include "ide-search-reducer.h"
 #include "ide-search-result.h"
 #include "ide-service.h"
 #include "ide-source-location.h"
@@ -95,7 +96,6 @@ G_BEGIN_DECLS
 
 #include "autotools/ide-autotools-build-system.h"
 #include "c/ide-c-language.h"
-#include "devhelp/ide-devhelp-search-result.h"
 #include "directory/ide-directory-build-system.h"
 #include "directory/ide-directory-vcs.h"
 #include "doap/ide-doap-person.h"
diff --git a/plugins/devhelp/Makefile.am b/plugins/devhelp/Makefile.am
index 27b3718..14dd0ce 100644
--- a/plugins/devhelp/Makefile.am
+++ b/plugins/devhelp/Makefile.am
@@ -14,12 +14,18 @@ libdevhelp_la_SOURCES = \
        gb-devhelp-view.h \
        gb-devhelp-resources.c \
        gb-devhelp-resources.h \
+       ide-devhelp-search-provider.c \
+       ide-devhelp-search-provider.h \
+       ide-devhelp-search-result.c \
+       ide-devhelp-search-result.h \
+       gb-devhelp-plugin.c \
        $(NULL)
 
 libdevhelp_la_CFLAGS = \
        $(BUILDER_CFLAGS) \
        -I$(top_srcdir)/src \
        -I$(top_srcdir)/src/documents \
+       -I$(top_srcdir)/src/search \
        -I$(top_srcdir)/src/views \
        -I$(top_srcdir)/src/util \
        -I$(top_srcdir)/src/workbench \
diff --git a/plugins/devhelp/gb-devhelp-panel.c b/plugins/devhelp/gb-devhelp-panel.c
index 4883fe3..f9f7101 100644
--- a/plugins/devhelp/gb-devhelp-panel.c
+++ b/plugins/devhelp/gb-devhelp-panel.c
@@ -121,6 +121,21 @@ gb_devhelp_panel_unload (GbWorkbenchAddin *addin)
   ide_clear_weak_pointer (&self->workbench);
 }
 
+void
+gb_devhelp_panel_set_uri (GbDevhelpPanel *self,
+                          const gchar    *uri)
+{
+  GbViewGrid *view_grid;
+
+  g_return_if_fail (GB_IS_DEVHELP_PANEL (self));
+
+  view_grid = GB_VIEW_GRID (gb_workbench_get_view_grid (self->workbench));
+
+  dh_sidebar_select_uri (DH_SIDEBAR (self->sidebar), uri);
+  gb_devhelp_document_set_uri (GB_DEVHELP_DOCUMENT (self->document), uri);
+  gb_view_grid_focus_document (view_grid, GB_DOCUMENT (self->document));
+}
+
 static void
 link_selected_cb (GbDevhelpPanel *self,
                   DhLink         *link,
@@ -134,6 +149,7 @@ link_selected_cb (GbDevhelpPanel *self,
   g_assert (DH_IS_SIDEBAR (sidebar));
 
   view_grid = GB_VIEW_GRID (gb_workbench_get_view_grid (self->workbench));
+
   uri = dh_link_get_uri (link);
   gb_devhelp_document_set_uri (GB_DEVHELP_DOCUMENT (self->document), uri);
   gb_view_grid_focus_document (view_grid, GB_DOCUMENT (self->document));
@@ -249,8 +265,3 @@ workbench_addin_iface_init (GbWorkbenchAddinInterface *iface)
   iface->load = gb_devhelp_panel_load;
   iface->unload = gb_devhelp_panel_unload;
 }
-
-GB_DEFINE_EMBEDDED_PLUGIN (gb_devhelp,
-                           gb_devhelp_get_resource (),
-                           "resource:///org/gnome/builder/plugins/devhelp/gb-devhelp.plugin",
-                           GB_DEFINE_PLUGIN_TYPE (GB_TYPE_WORKBENCH_ADDIN, GB_TYPE_DEVHELP_PANEL))
diff --git a/plugins/devhelp/gb-devhelp-panel.h b/plugins/devhelp/gb-devhelp-panel.h
index d36415b..7ad228c 100644
--- a/plugins/devhelp/gb-devhelp-panel.h
+++ b/plugins/devhelp/gb-devhelp-panel.h
@@ -27,6 +27,9 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (GbDevhelpPanel, gb_devhelp_panel, GB, DEVHELP_PANEL, GtkBin)
 
+void gb_devhelp_panel_set_uri (GbDevhelpPanel *self,
+                               const gchar    *uri);
+
 G_END_DECLS
 
 #endif /* GB_DEVHELP_PANEL_H */
diff --git a/libide/devhelp/ide-devhelp-search-result.h b/plugins/devhelp/gb-devhelp-plugin.c
similarity index 54%
copy from libide/devhelp/ide-devhelp-search-result.h
copy to plugins/devhelp/gb-devhelp-plugin.c
index 0b4a434..e81841e 100644
--- a/libide/devhelp/ide-devhelp-search-result.h
+++ b/plugins/devhelp/gb-devhelp-plugin.c
@@ -1,4 +1,4 @@
-/* ide-devhelp-search-result.h
+/* gb-devhelp-plugin.c
  *
  * Copyright (C) 2015 Christian Hergert <christian hergert me>
  *
@@ -16,18 +16,17 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef IDE_DEVHELP_SEARCH_RESULT_H
-#define IDE_DEVHELP_SEARCH_RESULT_H
+#include <ide.h>
 
-#include "ide-search-result.h"
+#include "gb-devhelp-panel.h"
+#include "gb-devhelp-resources.h"
+#include "gb-plugins.h"
+#include "gb-workbench-addin.h"
 
-G_BEGIN_DECLS
+#include "ide-devhelp-search-provider.h"
 
-#define IDE_TYPE_DEVHELP_SEARCH_RESULT (ide_devhelp_search_result_get_type())
-
-G_DECLARE_FINAL_TYPE (IdeDevhelpSearchResult, ide_devhelp_search_result,
-                      IDE, DEVHELP_SEARCH_RESULT, IdeSearchResult)
-
-G_END_DECLS
-
-#endif /* IDE_DEVHELP_SEARCH_RESULT_H */
+GB_DEFINE_EMBEDDED_PLUGIN (gb_devhelp,
+                           gb_devhelp_get_resource (),
+                           "resource:///org/gnome/builder/plugins/devhelp/gb-devhelp.plugin",
+                           GB_DEFINE_PLUGIN_TYPE (GB_TYPE_WORKBENCH_ADDIN, GB_TYPE_DEVHELP_PANEL)
+                           GB_DEFINE_PLUGIN_TYPE (IDE_TYPE_SEARCH_PROVIDER, 
IDE_TYPE_DEVHELP_SEARCH_PROVIDER))
diff --git a/libide/devhelp/ide-devhelp-search-provider.c b/plugins/devhelp/ide-devhelp-search-provider.c
similarity index 67%
rename from libide/devhelp/ide-devhelp-search-provider.c
rename to plugins/devhelp/ide-devhelp-search-provider.c
index b11cea1..dace3f8 100644
--- a/libide/devhelp/ide-devhelp-search-provider.c
+++ b/plugins/devhelp/ide-devhelp-search-provider.c
@@ -18,28 +18,35 @@
 
 #define G_LOG_DOMAIN "devhelp-search"
 
-#include "ide-devhelp-search-provider.h"
-
 #include <ctype.h>
 #include <glib/gi18n.h>
 #include <devhelp/devhelp.h>
+#include <ide.h>
 
+#include "ide-devhelp-search-provider.h"
 #include "ide-devhelp-search-result.h"
-#include "ide-search-reducer.h"
-#include "ide-search-result.h"
-#include "ide-search-context.h"
+
+#include "gb-devhelp-document.h"
+#include "gb-devhelp-panel.h"
+#include "gb-search-display-row.h"
+#include "gb-view-grid.h"
+#include "gb-widget.h"
+#include "gb-workbench.h"
+#include "gb-workspace.h"
 
 struct _IdeDevhelpSearchProvider
 {
-  IdeSearchProvider  parent;
+  IdeObject          parent;
 
   DhBookManager     *book_manager;
   DhKeywordModel    *keywords_model;
 };
 
-G_DEFINE_TYPE (IdeDevhelpSearchProvider, ide_devhelp_search_provider, IDE_TYPE_SEARCH_PROVIDER)
+static void search_provider_iface_init (IdeSearchProviderInterface *iface);
 
-static GQuark gQuarkLink;
+G_DEFINE_TYPE_WITH_CODE (IdeDevhelpSearchProvider, ide_devhelp_search_provider, IDE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_SEARCH_PROVIDER,
+                                                search_provider_iface_init))
 
 static void
 ide_devhelp_search_provider_populate (IdeSearchProvider *provider,
@@ -105,6 +112,7 @@ ide_devhelp_search_provider_populate (IdeSearchProvider *provider,
 
       result = g_object_new (IDE_TYPE_DEVHELP_SEARCH_RESULT,
                              "context", idecontext,
+                             "provider", provider,
                              "title", name,
                              "subtitle", dh_link_get_book_name (link),
                              "score", score,
@@ -132,6 +140,52 @@ ide_devhelp_search_provider_constructed (GObject *object)
   dh_keyword_model_set_words (self->keywords_model, self->book_manager);
 }
 
+static GtkWidget *
+ide_devhelp_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 (GB_TYPE_SEARCH_DISPLAY_ROW,
+                       "result", result,
+                       "visible", TRUE,
+                       NULL);
+}
+
+static void
+ide_devhelp_search_provider_activate (IdeSearchProvider *provider,
+                                      GtkWidget         *row,
+                                      IdeSearchResult   *result)
+{
+  g_autoptr(GbDocument) copy = NULL;
+  GbDevhelpPanel *panel;
+  GtkWidget *workspace;
+  GtkWidget *toplevel;
+  GtkWidget *pane;
+  gchar *uri;
+
+  g_return_if_fail (IDE_IS_DEVHELP_SEARCH_PROVIDER (provider));
+  g_return_if_fail (GTK_IS_WIDGET (row));
+  g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+
+  toplevel = gtk_widget_get_toplevel (row);
+
+  if (!GB_IS_WORKBENCH (toplevel))
+    return;
+
+  workspace = gb_workbench_get_workspace (GB_WORKBENCH (toplevel));
+  pane = gb_workspace_get_right_pane (GB_WORKSPACE (workspace));
+  panel = gb_widget_find_child_typed (pane, GB_TYPE_DEVHELP_PANEL);
+
+  g_object_get (result, "uri", &uri, NULL);
+
+  if (panel != NULL)
+    gb_devhelp_panel_set_uri (panel, uri);
+
+  g_free (uri);
+}
+
 static void
 ide_devhelp_search_provider_finalize (GObject *object)
 {
@@ -146,15 +200,9 @@ static void
 ide_devhelp_search_provider_class_init (IdeDevhelpSearchProviderClass *klass)
 {
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  IdeSearchProviderClass *provider_class = IDE_SEARCH_PROVIDER_CLASS (klass);
 
   object_class->constructed = ide_devhelp_search_provider_constructed;
   object_class->finalize = ide_devhelp_search_provider_finalize;
-
-  provider_class->get_verb = ide_devhelp_search_provider_get_verb;
-  provider_class->populate = ide_devhelp_search_provider_populate;
-
-  gQuarkLink = g_quark_from_static_string ("LINK");
 }
 
 static void
@@ -163,3 +211,12 @@ ide_devhelp_search_provider_init (IdeDevhelpSearchProvider *self)
   self->book_manager = dh_book_manager_new ();
   self->keywords_model = dh_keyword_model_new ();
 }
+
+static void
+search_provider_iface_init (IdeSearchProviderInterface *iface)
+{
+  iface->create_row = ide_devhelp_search_provider_create_row;
+  iface->get_verb = ide_devhelp_search_provider_get_verb;
+  iface->populate = ide_devhelp_search_provider_populate;
+  iface->activate = ide_devhelp_search_provider_activate;
+}
diff --git a/libide/devhelp/ide-devhelp-search-provider.h b/plugins/devhelp/ide-devhelp-search-provider.h
similarity index 78%
rename from libide/devhelp/ide-devhelp-search-provider.h
rename to plugins/devhelp/ide-devhelp-search-provider.h
index fda5f2f..67ba706 100644
--- a/libide/devhelp/ide-devhelp-search-provider.h
+++ b/plugins/devhelp/ide-devhelp-search-provider.h
@@ -25,16 +25,9 @@ G_BEGIN_DECLS
 
 #define IDE_TYPE_DEVHELP_SEARCH_PROVIDER (ide_devhelp_search_provider_get_type())
 
-G_DECLARE_FINAL_TYPE (IdeDevhelpSearchProvider,
-                      ide_devhelp_search_provider,
-                      IDE,
-                      DEVHELP_SEARCH_PROVIDER,
-                      IdeSearchProvider)
-
-struct _IdeDevhelpSearchProviderClass
-{
-  IdeSearchProviderClass parent;
-};
+G_DECLARE_FINAL_TYPE (IdeDevhelpSearchProvider, ide_devhelp_search_provider,
+                      IDE, DEVHELP_SEARCH_PROVIDER,
+                      IdeObject)
 
 G_END_DECLS
 
diff --git a/libide/devhelp/ide-devhelp-search-result.c b/plugins/devhelp/ide-devhelp-search-result.c
similarity index 100%
rename from libide/devhelp/ide-devhelp-search-result.c
rename to plugins/devhelp/ide-devhelp-search-result.c
diff --git a/libide/devhelp/ide-devhelp-search-result.h b/plugins/devhelp/ide-devhelp-search-result.h
similarity index 100%
rename from libide/devhelp/ide-devhelp-search-result.h
rename to plugins/devhelp/ide-devhelp-search-result.h
diff --git a/src/Makefile.am b/src/Makefile.am
index 717d123..cf44ab4 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -196,6 +196,7 @@ libgnome_builder_la_CFLAGS = \
        -I$(srcdir)/views \
        -I$(srcdir)/workbench \
        -I$(srcdir)/workspace \
+       -I$(top_builddir)/plugins/devhelp \
        -I$(top_builddir)/data/icons/hicolor \
        -I$(top_builddir)/libide \
        -I$(top_srcdir)/libide \
diff --git a/src/search/gb-search-box.c b/src/search/gb-search-box.c
index f4fe3d8..63fffb9 100644
--- a/src/search/gb-search-box.c
+++ b/src/search/gb-search-box.c
@@ -28,6 +28,9 @@
 #include "gb-widget.h"
 #include "gb-workbench.h"
 
+/* FIXME: make search result row creation pluggable */
+#include "ide-devhelp-search-result.h"
+
 #define SHORT_DELAY_TIMEOUT_MSEC 30
 #define LONG_DELAY_TIMEOUT_MSEC  30
 
@@ -101,7 +104,7 @@ gb_search_box_delay_cb (gpointer user_data)
     return G_SOURCE_REMOVE;
 
   /* TODO: Remove search text */
-  context = ide_search_engine_search (search_engine, NULL, search_text);
+  context = ide_search_engine_search (search_engine, search_text);
   gb_search_display_set_context (self->display, context);
   ide_search_context_execute (context, search_text, 5);
   g_object_unref (context);
diff --git a/src/search/gb-search-display-group.c b/src/search/gb-search-display-group.c
index 77fd009..02af027 100644
--- a/src/search/gb-search-display-group.c
+++ b/src/search/gb-search-display-group.c
@@ -117,23 +117,16 @@ gb_search_display_group_set_size_group (GbSearchDisplayGroup *self,
 GtkWidget *
 gb_search_display_group_create_row (IdeSearchResult *result)
 {
-  GtkListBoxRow *row;
-  GbSearchDisplayRow *disp_row;
+  IdeSearchProvider *provider;
+  GtkWidget *row;
 
   g_return_val_if_fail (IDE_IS_SEARCH_RESULT (result), NULL);
 
-  row = g_object_new (GTK_TYPE_LIST_BOX_ROW,
-                      "visible", TRUE,
-                      NULL);
-  disp_row = g_object_new (GB_TYPE_SEARCH_DISPLAY_ROW,
-                           "visible", TRUE,
-                           "result", result,
-                           NULL);
-  gtk_container_add (GTK_CONTAINER (row), GTK_WIDGET (disp_row));
-
+  provider = ide_search_result_get_provider (result);
+  row = ide_search_provider_create_row (provider, result);
   g_object_set_qdata (G_OBJECT (result), gQuarkRow, row);
 
-  return GTK_WIDGET (row);
+  return row;
 }
 
 void
@@ -198,8 +191,6 @@ compare_cb (GtkListBoxRow *row1,
             gpointer       user_data)
 {
   GtkListBoxRow *more_row = user_data;
-  GtkWidget *child1;
-  GtkWidget *child2;
   IdeSearchResult *result1;
   IdeSearchResult *result2;
   gfloat score1;
@@ -210,11 +201,8 @@ compare_cb (GtkListBoxRow *row1,
   else if (row2 == more_row)
     return -1;
 
-  child1 = gtk_bin_get_child (GTK_BIN (row1));
-  child2 = gtk_bin_get_child (GTK_BIN (row2));
-
-  result1 = gb_search_display_row_get_result (GB_SEARCH_DISPLAY_ROW (child1));
-  result2 = gb_search_display_row_get_result (GB_SEARCH_DISPLAY_ROW (child2));
+  result1 = gb_search_display_row_get_result (GB_SEARCH_DISPLAY_ROW (row1));
+  result2 = gb_search_display_row_get_result (GB_SEARCH_DISPLAY_ROW (row2));
 
   score1 = ide_search_result_get_score (result1);
   score2 = ide_search_result_get_score (result2);
@@ -227,6 +215,21 @@ compare_cb (GtkListBoxRow *row1,
     return 0;
 }
 
+static void
+gb_search_display_group_result_activated (GbSearchDisplayGroup *self,
+                                          GtkWidget            *widget,
+                                          IdeSearchResult      *result)
+{
+  IdeSearchProvider *provider;
+
+  g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (self));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
+
+  provider = ide_search_result_get_provider (result);
+  ide_search_provider_activate (provider, widget, result);
+}
+
 void
 gb_search_display_group_unselect (GbSearchDisplayGroup *self)
 {
@@ -240,22 +243,15 @@ gb_search_display_group_row_activated (GbSearchDisplayGroup *self,
                                        GtkListBoxRow        *row,
                                        GtkListBox           *list_box)
 {
-  GtkWidget *child;
+  IdeSearchResult *result;
 
   g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (self));
-  g_return_if_fail (!row || GTK_IS_LIST_BOX_ROW (row));
+  g_return_if_fail (GB_IS_SEARCH_DISPLAY_ROW (row));
   g_return_if_fail (GTK_IS_LIST_BOX (list_box));
 
-  child = gtk_bin_get_child (GTK_BIN (row));
-
-  if (GB_IS_SEARCH_DISPLAY_ROW (child))
-    {
-      IdeSearchResult *result;
-
-      result = gb_search_display_row_get_result (GB_SEARCH_DISPLAY_ROW (child));
-      if (result)
-        g_signal_emit (self, gSignals [RESULT_ACTIVATED], 0, result);
-    }
+  result = gb_search_display_row_get_result (GB_SEARCH_DISPLAY_ROW (row));
+  if (result)
+    g_signal_emit (self, gSignals [RESULT_ACTIVATED], 0, row, result);
 }
 
 static void
@@ -431,14 +427,15 @@ gb_search_display_group_class_init (GbSearchDisplayGroupClass *klass)
   g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
 
   gSignals [RESULT_ACTIVATED] =
-    g_signal_new ("result-activated",
-                  G_TYPE_FROM_CLASS (klass),
-                  G_SIGNAL_RUN_LAST,
-                  0,
-                  NULL, NULL, NULL,
-                  G_TYPE_NONE,
-                  1,
-                  IDE_TYPE_SEARCH_RESULT);
+    g_signal_new_class_handler ("result-activated",
+                                G_TYPE_FROM_CLASS (klass),
+                                G_SIGNAL_RUN_LAST,
+                                G_CALLBACK (gb_search_display_group_result_activated),
+                                NULL, NULL, NULL,
+                                G_TYPE_NONE,
+                                2,
+                                GTK_TYPE_WIDGET,
+                                IDE_TYPE_SEARCH_RESULT);
 
   gSignals [RESULT_SELECTED] =
     g_signal_new ("result-selected",
diff --git a/src/search/gb-search-display-row.c b/src/search/gb-search-display-row.c
index 4f881c9..a90f399 100644
--- a/src/search/gb-search-display-row.c
+++ b/src/search/gb-search-display-row.c
@@ -33,7 +33,7 @@ struct _GbSearchDisplayRow
   GtkProgressBar  *progress;
 };
 
-G_DEFINE_TYPE (GbSearchDisplayRow, gb_search_display_row, GTK_TYPE_BOX)
+G_DEFINE_TYPE (GbSearchDisplayRow, gb_search_display_row, GTK_TYPE_LIST_BOX_ROW)
 
 enum {
   PROP_0,
diff --git a/src/search/gb-search-display-row.h b/src/search/gb-search-display-row.h
index 649a8a2..404ee7d 100644
--- a/src/search/gb-search-display-row.h
+++ b/src/search/gb-search-display-row.h
@@ -26,7 +26,9 @@ G_BEGIN_DECLS
 
 #define GB_TYPE_SEARCH_DISPLAY_ROW (gb_search_display_row_get_type())
 
-G_DECLARE_FINAL_TYPE (GbSearchDisplayRow, gb_search_display_row, GB, SEARCH_DISPLAY_ROW, GtkBox)
+G_DECLARE_FINAL_TYPE (GbSearchDisplayRow, gb_search_display_row,
+                      GB, SEARCH_DISPLAY_ROW,
+                      GtkListBoxRow)
 
 IdeSearchResult *gb_search_display_row_get_result (GbSearchDisplayRow *row);
 void             gb_search_display_row_set_result (GbSearchDisplayRow *row,
diff --git a/src/search/gb-search-display.c b/src/search/gb-search-display.c
index 43d2ec7..a732602 100644
--- a/src/search/gb-search-display.c
+++ b/src/search/gb-search-display.c
@@ -104,11 +104,13 @@ gb_search_display_real_result_activated (GbSearchDisplay *self,
 
 static void
 gb_search_display_result_activated (GbSearchDisplay      *self,
+                                    GtkWidget            *widget,
                                     IdeSearchResult      *result,
                                     GbSearchDisplayGroup *group)
 {
   g_return_if_fail (GB_IS_SEARCH_DISPLAY (self));
-  g_return_if_fail (!result || IDE_IS_SEARCH_RESULT (result));
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+  g_return_if_fail (IDE_IS_SEARCH_RESULT (result));
   g_return_if_fail (GB_IS_SEARCH_DISPLAY_GROUP (group));
 
   g_signal_emit (self, gSignals [RESULT_ACTIVATED], 0, result);
diff --git a/src/util/gb-widget.c b/src/util/gb-widget.c
index 66c10a8..71aeb73 100644
--- a/src/util/gb-widget.c
+++ b/src/util/gb-widget.c
@@ -332,3 +332,49 @@ gb_widget_set_context_handler (gpointer               widget,
   if ((toplevel = gtk_widget_get_toplevel (widget)))
     gb_widget_hierarchy_changed (widget, NULL, NULL);
 }
+
+static void
+gb_widget_find_child_typed_cb (GtkWidget *widget,
+                               gpointer   user_data)
+{
+  struct {
+    gpointer ret;
+    GType type;
+  } *state = user_data;
+
+  if (state->ret != NULL)
+    return;
+
+  if (g_type_is_a (G_OBJECT_TYPE (widget), state->type))
+    {
+      state->ret = widget;
+    }
+  else if (GTK_IS_CONTAINER (widget))
+    {
+      gtk_container_foreach (GTK_CONTAINER (widget),
+                             gb_widget_find_child_typed_cb,
+                             state);
+    }
+}
+
+gpointer
+gb_widget_find_child_typed (GtkWidget *widget,
+                            GType      child_type)
+{
+  struct {
+    gpointer ret;
+    GType type;
+  } state;
+
+  g_return_val_if_fail (GTK_IS_CONTAINER (widget), NULL);
+  g_return_val_if_fail (g_type_is_a (child_type, GTK_TYPE_WIDGET), NULL);
+
+  state.ret = NULL;
+  state.type = child_type;
+
+  gtk_container_foreach (GTK_CONTAINER (widget),
+                         gb_widget_find_child_typed_cb,
+                         &state);
+
+  return state.ret;
+}
diff --git a/src/util/gb-widget.h b/src/util/gb-widget.h
index 3146bfe..81917df 100644
--- a/src/util/gb-widget.h
+++ b/src/util/gb-widget.h
@@ -54,6 +54,8 @@ gboolean         gb_widget_activate_action     (GtkWidget    *widget,
                                                 GVariant     *parameter);
 void             gb_widget_set_context_handler (gpointer      widget,
                                                 GbWidgetContextHandler handler);
+gpointer         gb_widget_find_child_typed    (GtkWidget    *widget,
+                                                GType         child_type);
 
 G_END_DECLS
 
diff --git a/tools/ide-search.c b/tools/ide-search.c
index 18eb5bc..1c34120 100644
--- a/tools/ide-search.c
+++ b/tools/ide-search.c
@@ -89,7 +89,7 @@ context_cb (GObject      *object,
     }
 
   search_engine = ide_context_get_search_engine (context);
-  search_context = ide_search_engine_search (search_engine, NULL, gSearchTerms);
+  search_context = ide_search_engine_search (search_engine, gSearchTerms);
   /* FIXME: ^ search terms duplicated */
 
   g_signal_connect (search_context, "result-added",


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