[gnome-builder] libide/search: wrap search engine results in IdeSearchResults
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder] libide/search: wrap search engine results in IdeSearchResults
- Date: Sat, 16 Jul 2022 00:27:15 +0000 (UTC)
commit 5fa89d9766650b91db8318807f1bcdb0f144e535
Author: Christian Hergert <chergert redhat com>
Date: Fri Jul 15 17:03:43 2022 -0700
libide/search: wrap search engine results in IdeSearchResults
This will allow us to do followup filtering without having to requery
the individual search providers. That's useful as currently we have to
inflate all the search results anyway, and so we'd like to avoid doing
that on most keypresses.
Instead, the popover (in a followup commit) will be able to simply
refilter the previous set and only if it fails, discard the result.
src/libide/gui/ide-search-popover.c | 6 +-
src/libide/search/ide-search-engine.c | 42 ++++--
src/libide/search/ide-search-engine.h | 33 ++---
src/libide/search/ide-search-result.h | 6 +-
src/libide/search/ide-search-results-private.h | 30 +++++
src/libide/search/ide-search-results.c | 173 +++++++++++++++++++++++++
src/libide/search/ide-search-results.h | 40 ++++++
src/libide/search/libide-search.h | 1 +
src/libide/search/meson.build | 2 +
9 files changed, 299 insertions(+), 34 deletions(-)
---
diff --git a/src/libide/gui/ide-search-popover.c b/src/libide/gui/ide-search-popover.c
index 69093c62d..aed346b63 100644
--- a/src/libide/gui/ide-search-popover.c
+++ b/src/libide/gui/ide-search-popover.c
@@ -117,7 +117,7 @@ ide_search_popover_search_cb (GObject *object,
{
IdeSearchEngine *search_engine = (IdeSearchEngine *)object;
g_autoptr(IdeSearchPopover) self = user_data;
- g_autoptr(GListModel) results = NULL;
+ g_autoptr(IdeSearchResults) results = NULL;
g_autoptr(GError) error = NULL;
IDE_ENTRY;
@@ -132,13 +132,13 @@ ide_search_popover_search_cb (GObject *object,
if (error != NULL)
g_debug ("Search failed: %s", error->message);
- gtk_single_selection_set_model (self->selection, results);
+ gtk_single_selection_set_model (self->selection, G_LIST_MODEL (results));
if (self->activate_after_search)
{
self->activate_after_search = FALSE;
- if (results != NULL && g_list_model_get_n_items (results) > 0)
+ if (results != NULL && g_list_model_get_n_items (G_LIST_MODEL (results)) > 0)
{
IdeSearchResult *selected = gtk_single_selection_get_selected_item (self->selection);
diff --git a/src/libide/search/ide-search-engine.c b/src/libide/search/ide-search-engine.c
index 580a35f27..83237587c 100644
--- a/src/libide/search/ide-search-engine.c
+++ b/src/libide/search/ide-search-engine.c
@@ -30,6 +30,7 @@
#include "ide-search-engine.h"
#include "ide-search-provider.h"
#include "ide-search-result.h"
+#include "ide-search-results-private.h"
#define DEFAULT_MAX_RESULTS 50
@@ -43,11 +44,11 @@ struct _IdeSearchEngine
typedef struct
{
- IdeTask *task;
- gchar *query;
- GListStore *store;
- guint outstanding;
- guint max_results;
+ IdeTask *task;
+ char *query;
+ GListStore *store;
+ guint outstanding;
+ guint max_results;
} Request;
enum {
@@ -258,11 +259,14 @@ cleanup:
r->outstanding--;
if (r->outstanding == 0)
- ide_task_return_pointer (task,
- g_object_new (GTK_TYPE_FLATTEN_LIST_MODEL,
- "model", r->store,
- NULL),
- g_object_unref);
+ {
+ g_autoptr(GtkFlattenListModel) flatten = NULL;
+
+ flatten = gtk_flatten_list_model_new (G_LIST_MODEL (g_steal_pointer (&r->store)));
+ ide_task_return_pointer (task,
+ _ide_search_results_new (G_LIST_MODEL (flatten), r->query),
+ g_object_unref);
+ }
}
static void
@@ -322,7 +326,7 @@ ide_search_engine_search_foreach_custom_provider (gpointer data,
void
ide_search_engine_search_async (IdeSearchEngine *self,
- const gchar *query,
+ const char *query,
guint max_results,
GCancellable *cancellable,
GAsyncReadyCallback callback,
@@ -372,19 +376,29 @@ ide_search_engine_search_async (IdeSearchEngine *self,
*
* Completes an asynchronous request to ide_search_engine_search_async().
*
- * The result is a #GListModel of #IdeSearchResult when successful.
+ * The result is a #GListModel of #IdeSearchResult when successful. The type
+ * is #IdeSearchResults which allows you to do additional filtering on the
+ * result set instead of querying providers again.
*
* Returns: (transfer full): a #GListModel of #IdeSearchResult items.
*/
-GListModel *
+IdeSearchResults *
ide_search_engine_search_finish (IdeSearchEngine *self,
GAsyncResult *result,
GError **error)
{
+ IdeSearchResults *ret;
+
+ IDE_ENTRY;
+
g_return_val_if_fail (IDE_IS_SEARCH_ENGINE (self), NULL);
g_return_val_if_fail (IDE_IS_TASK (result), NULL);
- return ide_task_propagate_pointer (IDE_TASK (result), error);
+ ret = ide_task_propagate_pointer (IDE_TASK (result), error);
+
+ g_return_val_if_fail (!ret || IDE_IS_SEARCH_RESULTS (ret), NULL);
+
+ IDE_RETURN (ret);
}
/**
diff --git a/src/libide/search/ide-search-engine.h b/src/libide/search/ide-search-engine.h
index 7e513b01e..477918e8a 100644
--- a/src/libide/search/ide-search-engine.h
+++ b/src/libide/search/ide-search-engine.h
@@ -25,7 +25,9 @@
#endif
#include <libide-core.h>
+
#include "ide-search-provider.h"
+#include "ide-search-results.h"
G_BEGIN_DECLS
@@ -33,26 +35,27 @@ G_BEGIN_DECLS
IDE_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (IdeSearchEngine, ide_search_engine, IDE, SEARCH_ENGINE, IdeObject)
+
IDE_AVAILABLE_IN_ALL
-IdeSearchEngine *ide_search_engine_new (void);
+IdeSearchEngine *ide_search_engine_new (void);
IDE_AVAILABLE_IN_ALL
-gboolean ide_search_engine_get_busy (IdeSearchEngine *self);
+gboolean ide_search_engine_get_busy (IdeSearchEngine *self);
IDE_AVAILABLE_IN_ALL
-void ide_search_engine_search_async (IdeSearchEngine *self,
- const gchar *query,
- guint max_results,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
+void ide_search_engine_search_async (IdeSearchEngine *self,
+ const char *query,
+ guint max_results,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
IDE_AVAILABLE_IN_ALL
-GListModel *ide_search_engine_search_finish (IdeSearchEngine *self,
- GAsyncResult *result,
- GError **error);
+IdeSearchResults *ide_search_engine_search_finish (IdeSearchEngine *self,
+ GAsyncResult *result,
+ GError **error);
IDE_AVAILABLE_IN_ALL
-void ide_search_engine_add_provider (IdeSearchEngine *self,
- IdeSearchProvider *provider);
+void ide_search_engine_add_provider (IdeSearchEngine *self,
+ IdeSearchProvider *provider);
IDE_AVAILABLE_IN_ALL
-void ide_search_engine_remove_provider (IdeSearchEngine *self,
- IdeSearchProvider *provider);
+void ide_search_engine_remove_provider (IdeSearchEngine *self,
+ IdeSearchProvider *provider);
G_END_DECLS
diff --git a/src/libide/search/ide-search-result.h b/src/libide/search/ide-search-result.h
index b33e92dea..681a052a9 100644
--- a/src/libide/search/ide-search-result.h
+++ b/src/libide/search/ide-search-result.h
@@ -39,8 +39,10 @@ struct _IdeSearchResultClass
{
GObjectClass parent_class;
- void (*activate) (IdeSearchResult *self,
- GtkWidget *last_focus);
+ gboolean (*matches) (IdeSearchResult *self,
+ const char *query);
+ void (*activate) (IdeSearchResult *self,
+ GtkWidget *last_focus);
};
IDE_AVAILABLE_IN_ALL
diff --git a/src/libide/search/ide-search-results-private.h b/src/libide/search/ide-search-results-private.h
new file mode 100644
index 000000000..dd19016b9
--- /dev/null
+++ b/src/libide/search/ide-search-results-private.h
@@ -0,0 +1,30 @@
+/* ide-search-results-private.h
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-search-results.h"
+
+G_BEGIN_DECLS
+
+IdeSearchResults *_ide_search_results_new (GListModel *model,
+ const char *query);
+
+G_END_DECLS
diff --git a/src/libide/search/ide-search-results.c b/src/libide/search/ide-search-results.c
new file mode 100644
index 000000000..d609a015e
--- /dev/null
+++ b/src/libide/search/ide-search-results.c
@@ -0,0 +1,173 @@
+/* ide-search-results.c
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-search-results"
+
+#include "config.h"
+
+#include <gtk/gtk.h>
+
+#include "ide-search-result.h"
+#include "ide-search-results-private.h"
+
+struct _IdeSearchResults
+{
+ GObject parent_instance;
+
+ /* The model doing our filtering, we proxy through to it */
+ GtkFilterListModel *filter_model;
+ GtkCustomFilter *filter;
+
+ /* The current refiltered word, >= @query with matching prefix */
+ char *refilter;
+
+ /* The original query and it's length */
+ char *query;
+ gsize query_len;
+};
+
+static GType
+ide_search_results_get_item_type (GListModel *model)
+{
+ return IDE_TYPE_SEARCH_RESULT;
+}
+
+static guint
+ide_search_results_get_n_items (GListModel *model)
+{
+ return g_list_model_get_n_items (G_LIST_MODEL (IDE_SEARCH_RESULTS (model)->filter_model));
+}
+
+static gpointer
+ide_search_results_get_item (GListModel *model,
+ guint position)
+{
+ return g_list_model_get_item (G_LIST_MODEL (IDE_SEARCH_RESULTS (model)->filter_model), position);
+}
+
+static void
+list_model_iface_init (GListModelInterface *iface)
+{
+ iface->get_item_type = ide_search_results_get_item_type;
+ iface->get_n_items = ide_search_results_get_n_items;
+ iface->get_item = ide_search_results_get_item;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (IdeSearchResults, ide_search_results, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, list_model_iface_init))
+
+static void
+ide_search_results_dispose (GObject *object)
+{
+ IdeSearchResults *self = (IdeSearchResults *)object;
+
+ if (self->filter_model != NULL)
+ {
+ gtk_filter_list_model_set_filter (self->filter_model, NULL);
+ g_clear_object (&self->filter_model);
+ }
+
+ g_clear_object (&self->filter);
+
+ g_clear_pointer (&self->query, g_free);
+ g_clear_pointer (&self->refilter, g_free);
+
+ G_OBJECT_CLASS (ide_search_results_parent_class)->dispose (object);
+}
+
+static void
+ide_search_results_class_init (IdeSearchResultsClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->dispose = ide_search_results_dispose;
+}
+
+static void
+ide_search_results_init (IdeSearchResults *self)
+{
+}
+
+static gboolean
+ide_search_results_filter (gpointer item,
+ gpointer user_data)
+{
+ IdeSearchResult *result = item;
+ IdeSearchResults *self = user_data;
+
+ return IDE_SEARCH_RESULT_GET_CLASS (result)->matches (result, self->refilter);
+}
+
+IdeSearchResults *
+_ide_search_results_new (GListModel *model,
+ const char *query)
+{
+ IdeSearchResults *self;
+
+ g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
+ g_return_val_if_fail (query != NULL, NULL);
+
+ self = g_object_new (IDE_TYPE_SEARCH_RESULTS, NULL);
+ self->query = g_strdup (query);
+ self->query_len = strlen (query);
+ self->refilter = g_strdup (query);
+ self->filter = gtk_custom_filter_new (ide_search_results_filter, self, NULL);
+ self->filter_model = gtk_filter_list_model_new (g_object_ref (model), NULL);
+
+ gtk_filter_list_model_set_incremental (self->filter_model, TRUE);
+
+ g_signal_connect_object (self->filter_model,
+ "items-changed",
+ G_CALLBACK (g_list_model_items_changed),
+ self,
+ G_CONNECT_SWAPPED);
+
+ return self;
+}
+
+gboolean
+ide_search_results_refilter (IdeSearchResults *self,
+ const char *query)
+{
+ g_autofree char *old_query = NULL;
+
+ g_return_val_if_fail (IDE_IS_SEARCH_RESULTS (self), FALSE);
+ g_return_val_if_fail (query != NULL, FALSE);
+
+ /* Make sure we have the prefix of the original search */
+ if (memcmp (self->query, query, self->query_len) != 0)
+ return FALSE;
+
+ /* Swap the filtering query */
+ old_query = g_steal_pointer (&self->refilter);
+ self->refilter = g_strdup (query);
+
+ /* Notify of changes, and only update the portion not matched */
+ if (g_str_has_prefix (query, old_query))
+ gtk_filter_changed (GTK_FILTER (self->filter), GTK_FILTER_CHANGE_MORE_STRICT);
+ else
+ gtk_filter_changed (GTK_FILTER (self->filter), GTK_FILTER_CHANGE_LESS_STRICT);
+
+ /* Attach filter if we haven't yet */
+ if (gtk_filter_list_model_get_filter (self->filter_model) == NULL)
+ gtk_filter_list_model_set_filter (self->filter_model, GTK_FILTER (self->filter));
+
+ return TRUE;
+}
diff --git a/src/libide/search/ide-search-results.h b/src/libide/search/ide-search-results.h
new file mode 100644
index 000000000..54fd8a97a
--- /dev/null
+++ b/src/libide/search/ide-search-results.h
@@ -0,0 +1,40 @@
+/* ide-search-results.h
+ *
+ * Copyright 2022 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#if !defined (IDE_SEARCH_INSIDE) && !defined (IDE_SEARCH_COMPILATION)
+# error "Only <libide-search.h> can be included directly."
+#endif
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_SEARCH_RESULTS (ide_search_results_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_FINAL_TYPE (IdeSearchResults, ide_search_results, IDE, SEARCH_RESULTS, GObject)
+
+IDE_AVAILABLE_IN_ALL
+gboolean ide_search_results_refilter (IdeSearchResults *self,
+ const char *query);
+
+G_END_DECLS
diff --git a/src/libide/search/libide-search.h b/src/libide/search/libide-search.h
index 23d9b11b3..6c4e8270b 100644
--- a/src/libide/search/libide-search.h
+++ b/src/libide/search/libide-search.h
@@ -34,4 +34,5 @@
# include "ide-search-provider.h"
# include "ide-search-reducer.h"
# include "ide-search-result.h"
+# include "ide-search-results.h"
#undef IDE_SEARCH_INSIDE
diff --git a/src/libide/search/meson.build b/src/libide/search/meson.build
index 551701aaf..de7ee7f56 100644
--- a/src/libide/search/meson.build
+++ b/src/libide/search/meson.build
@@ -17,6 +17,7 @@ libide_search_public_headers = [
'ide-search-provider.h',
'ide-search-reducer.h',
'ide-search-result.h',
+ 'ide-search-results.h',
'libide-search.h',
]
@@ -37,6 +38,7 @@ libide_search_public_sources = [
'ide-search-provider.c',
'ide-search-reducer.c',
'ide-search-result.c',
+ 'ide-search-results.c',
]
libide_search_private_sources = [
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]