[gnome-builder/wip/plugins] file-search: implement file search by scanning project directory
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/plugins] file-search: implement file search by scanning project directory
- Date: Sat, 13 Jun 2015 04:29:07 +0000 (UTC)
commit 669f86e75a665993422726730266149527ebf356
Author: Christian Hergert <christian hergert me>
Date: Fri Jun 12 21:24:23 2015 -0700
file-search: implement file search by scanning project directory
This replaces the git-only search engine. It walks the directory tree
ignoring files that are not of importance according to the IdeVcs.
The Vcs lookup probably needs some safety added to it, but since we are
in the context init path, right now it is fine.
We still need to monitor files for changes, but we can add that later.
plugins/file-search/Makefile.am | 2 +
plugins/file-search/gb-file-search-index.c | 245 ++++++++++++++++++++++++-
plugins/file-search/gb-file-search-index.h | 16 ++-
plugins/file-search/gb-file-search-provider.c | 106 +++++++++++-
plugins/file-search/gb-file-search-result.c | 107 +++++++++++
5 files changed, 471 insertions(+), 5 deletions(-)
---
diff --git a/plugins/file-search/Makefile.am b/plugins/file-search/Makefile.am
index f7d5091..986e8df 100644
--- a/plugins/file-search/Makefile.am
+++ b/plugins/file-search/Makefile.am
@@ -24,6 +24,8 @@ libfile_search_la_CFLAGS = \
$(BUILDER_CFLAGS) \
-I$(top_srcdir)/libide \
-I$(top_srcdir)/src \
+ -I$(top_srcdir)/src/search \
+ -I$(top_srcdir)/src/workbench \
-I$(top_srcdir)/contrib/search \
$(NULL)
diff --git a/plugins/file-search/gb-file-search-index.c b/plugins/file-search/gb-file-search-index.c
index 46a2c34..1844c26 100644
--- a/plugins/file-search/gb-file-search-index.c
+++ b/plugins/file-search/gb-file-search-index.c
@@ -18,19 +18,21 @@
#include <fuzzy.h>
#include <glib/gi18n.h>
+#include <ide.h>
#include "gb-file-search-index.h"
+#include "gb-file-search-result.h"
struct _GbFileSearchIndex
{
- GObject parent_instance;
+ IdeObject parent_instance;
GFile *root_directory;
GFileMonitor *file_monitor;
Fuzzy *fuzzy;
};
-G_DEFINE_TYPE (GbFileSearchIndex, gb_file_search_index, G_TYPE_OBJECT)
+G_DEFINE_TYPE (GbFileSearchIndex, gb_file_search_index, IDE_TYPE_OBJECT)
enum {
PROP_0,
@@ -62,6 +64,7 @@ gb_file_search_index_finalize (GObject *object)
g_clear_object (&self->root_directory);
g_clear_object (&self->file_monitor);
+ g_clear_pointer (&self->fuzzy, fuzzy_unref);
G_OBJECT_CLASS (gb_file_search_index_parent_class)->finalize (object);
}
@@ -127,3 +130,241 @@ static void
gb_file_search_index_init (GbFileSearchIndex *self)
{
}
+
+static void
+populate_from_dir (Fuzzy *fuzzy,
+ IdeVcs *vcs,
+ const gchar *relpath,
+ GFile *directory,
+ GCancellable *cancellable)
+{
+ GFileEnumerator *enumerator;
+ GPtrArray *children = NULL;
+ gpointer file_info_ptr;
+
+ g_assert (fuzzy != NULL);
+ g_assert (G_IS_FILE (directory));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ enumerator = g_file_enumerate_children (directory,
+ G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME","
+ G_FILE_ATTRIBUTE_STANDARD_TYPE,
+ G_FILE_QUERY_INFO_NONE,
+ cancellable,
+ NULL);
+
+ if (enumerator == NULL)
+ return;
+
+ while ((file_info_ptr = g_file_enumerator_next_file (enumerator, cancellable, NULL)))
+ {
+ g_autoptr(GFileInfo) file_info = file_info_ptr;
+ g_autofree gchar *path = NULL;
+ g_autoptr(GFile) file = NULL;
+ const gchar *name;
+
+ name = g_file_info_get_display_name (file_info);
+ file = g_file_get_child (directory, name);
+
+ if (g_file_info_get_file_type (file_info) == G_FILE_TYPE_DIRECTORY)
+ {
+ if (children == NULL)
+ children = g_ptr_array_new_with_free_func (g_object_unref);
+ g_ptr_array_add (children, g_object_ref (file));
+ continue;
+ }
+
+ if (ide_vcs_is_ignored (vcs, file, NULL))
+ continue;
+
+ if (relpath != NULL)
+ name = path = g_build_filename (relpath, name, NULL);
+
+ fuzzy_insert (fuzzy, name, NULL);
+ }
+
+ g_clear_object (&enumerator);
+
+ if (children != NULL)
+ {
+ gsize i;
+
+ for (i = 0; i < children->len; i++)
+ {
+ g_autofree gchar *path = NULL;
+ g_autofree gchar *name = NULL;
+ GFile *child;
+
+ child = g_ptr_array_index (children, i);
+ name = g_file_get_basename (child);
+
+ if (relpath != NULL)
+ path = g_build_filename (relpath, name, NULL);
+
+ populate_from_dir (fuzzy, vcs, path ? path : name, child, cancellable);
+ }
+ }
+
+ g_clear_pointer (&children, g_ptr_array_unref);
+}
+
+static void
+gb_file_search_index_builder (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GbFileSearchIndex *self = source_object;
+ GFile *directory = task_data;
+ IdeContext *context;
+ IdeVcs *vcs;
+ Fuzzy *fuzzy;
+ GTimer *timer;
+ gdouble elapsed;
+
+ g_assert (G_IS_TASK (task));
+ g_assert (GB_IS_FILE_SEARCH_INDEX (self));
+ g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ g_assert (G_IS_FILE (directory));
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ vcs = ide_context_get_vcs (context);
+
+ timer = g_timer_new ();
+
+ fuzzy = fuzzy_new (FALSE);
+ fuzzy_begin_bulk_insert (fuzzy);
+ populate_from_dir (fuzzy, vcs, NULL, directory, cancellable);
+ fuzzy_end_bulk_insert (fuzzy);
+
+ self->fuzzy = fuzzy;
+
+ g_timer_stop (timer);
+ elapsed = g_timer_elapsed (timer, NULL);
+
+ g_message ("File index built in %lf seconds.", elapsed);
+
+ g_task_return_boolean (task, TRUE);
+}
+
+void
+gb_file_search_index_build_async (GbFileSearchIndex *self,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_autoptr(GTask) task = NULL;
+
+ g_return_if_fail (GB_IS_FILE_SEARCH_INDEX (self));
+ g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+ task = g_task_new (self, cancellable, callback, user_data);
+
+ if (self->root_directory == NULL)
+ {
+ g_task_return_new_error (task,
+ G_IO_ERROR,
+ G_IO_ERROR_INVALID_FILENAME,
+ "Root directory has not been set.");
+ return;
+ }
+
+ g_task_set_task_data (task, g_object_ref (self->root_directory), g_object_unref);
+ g_task_run_in_thread (task, gb_file_search_index_builder);
+}
+
+gboolean
+gb_file_search_index_build_finish (GbFileSearchIndex *self,
+ GAsyncResult *result,
+ GError **error)
+{
+ GTask *task = (GTask *)result;
+
+ g_return_val_if_fail (GB_IS_FILE_SEARCH_INDEX (self), FALSE);
+ g_return_val_if_fail (G_IS_TASK (result), FALSE);
+ g_return_val_if_fail (G_IS_TASK (task), FALSE);
+
+ return g_task_propagate_boolean (task, error);
+}
+
+static gchar *
+str_highlight (const gchar *str,
+ const gchar *match)
+{
+ GString *ret;
+ gunichar str_ch;
+ gunichar match_ch;
+
+ ret = g_string_new (NULL);
+
+ for (; *str; str = g_utf8_next_char (str))
+ {
+ str_ch = g_utf8_get_char (str);
+ match_ch = g_utf8_get_char (match);
+
+ if (str_ch == match_ch)
+ {
+ g_string_append (ret, "<u>");
+ g_string_append_unichar (ret, str_ch);
+ g_string_append (ret, "</u>");
+
+ match = g_utf8_next_char (match);
+ }
+ else
+ {
+ g_string_append_unichar (ret, str_ch);
+ }
+ }
+
+ return g_string_free (ret, FALSE);
+}
+
+void
+gb_file_search_index_populate (GbFileSearchIndex *self,
+ IdeSearchContext *context,
+ IdeSearchProvider *provider,
+ const gchar *query)
+{
+ g_autoptr(GArray) ar = NULL;
+ g_auto(IdeSearchReducer) reducer = { 0 };
+ 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);
+
+ if (self->fuzzy == NULL)
+ return;
+
+ 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);
+
+ ar = fuzzy_match (self->fuzzy, query, max_matches);
+
+ for (i = 0; i < ar->len; i++)
+ {
+ FuzzyMatch *match;
+
+ match = &g_array_index (ar, FuzzyMatch, i);
+
+ if (ide_search_reducer_accepts (&reducer, match->score))
+ {
+ g_autoptr(GbFileSearchResult) result = NULL;
+ g_autofree gchar *markup = NULL;
+
+ markup = str_highlight (match->key, query);
+ result = g_object_new (GB_TYPE_FILE_SEARCH_RESULT,
+ "context", icontext,
+ "provider", provider,
+ "score", match->score,
+ "title", markup,
+ "path", match->key,
+ NULL);
+ ide_search_reducer_push (&reducer, IDE_SEARCH_RESULT (result));
+ }
+ }
+}
diff --git a/plugins/file-search/gb-file-search-index.h b/plugins/file-search/gb-file-search-index.h
index 97a881d..51339de 100644
--- a/plugins/file-search/gb-file-search-index.h
+++ b/plugins/file-search/gb-file-search-index.h
@@ -19,13 +19,25 @@
#ifndef GB_FILE_SEARCH_INDEX_H
#define GB_FILE_SEARCH_INDEX_H
-#include <gio/gio.h>
+#include <ide.h>
G_BEGIN_DECLS
#define GB_TYPE_FILE_SEARCH_INDEX (gb_file_search_index_get_type())
-G_DECLARE_FINAL_TYPE (GbFileSearchIndex, gb_file_search_index, GB, FILE_SEARCH_INDEX, GObject)
+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);
G_END_DECLS
diff --git a/plugins/file-search/gb-file-search-provider.c b/plugins/file-search/gb-file-search-provider.c
index 22cd514..89fce9d 100644
--- a/plugins/file-search/gb-file-search-provider.c
+++ b/plugins/file-search/gb-file-search-provider.c
@@ -19,10 +19,14 @@
#include <glib/gi18n.h>
#include "gb-file-search-provider.h"
+#include "gb-file-search-index.h"
+#include "gb-search-display-row.h"
+#include "gb-workbench.h"
struct _GbFileSearchProvider
{
- IdeObject parent_instance;
+ IdeObject parent_instance;
+ GbFileSearchIndex *index;
};
static void search_provider_iface_init (IdeSearchProviderInterface *iface);
@@ -44,17 +48,114 @@ gb_file_search_provider_populate (IdeSearchProvider *provider,
gsize max_results,
GCancellable *cancellable)
{
+ GbFileSearchProvider *self = (GbFileSearchProvider *)provider;
+
g_assert (IDE_IS_SEARCH_PROVIDER (provider));
g_assert (IDE_IS_SEARCH_CONTEXT (context));
g_assert (search_terms != NULL);
g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+ if (self->index != NULL)
+ gb_file_search_index_populate (self->index, context, provider, search_terms);
+
ide_search_context_provider_completed (context, provider);
}
static void
+gb_file_search_provider_build_cb (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GbFileSearchIndex *index = (GbFileSearchIndex *)object;
+ g_autoptr(GbFileSearchProvider) self = user_data;
+ GError *error = NULL;
+
+ g_assert (GB_IS_FILE_SEARCH_INDEX (index));
+ g_assert (GB_IS_FILE_SEARCH_PROVIDER (self));
+
+ if (!gb_file_search_index_build_finish (index, result, &error))
+ {
+ g_warning ("%s", error->message);
+ g_clear_error (&error);
+ }
+}
+
+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 (GB_TYPE_SEARCH_DISPLAY_ROW,
+ "result", result,
+ "visible", TRUE,
+ NULL);
+}
+
+static void
+gb_file_search_provider_activate (IdeSearchProvider *provider,
+ GtkWidget *row,
+ IdeSearchResult *result)
+{
+ GtkWidget *toplevel;
+
+ g_assert (IDE_IS_SEARCH_PROVIDER (provider));
+ g_assert (GTK_IS_WIDGET (row));
+ g_assert (IDE_IS_SEARCH_RESULT (result));
+
+ toplevel = gtk_widget_get_toplevel (row);
+
+ if (GB_IS_WORKBENCH (toplevel))
+ {
+ g_autofree gchar *path = NULL;
+ g_autoptr(GFile) file = NULL;
+ IdeContext *context;
+ IdeVcs *vcs;
+ GFile *workdir;
+
+ context = gb_workbench_get_context (GB_WORKBENCH (toplevel));
+ vcs = ide_context_get_vcs (context);
+ workdir = ide_vcs_get_working_directory (vcs);
+ g_object_get (result, "path", &path, NULL);
+ file = g_file_get_child (workdir, path);
+
+ gb_workbench_open (GB_WORKBENCH (toplevel), file);
+ }
+}
+
+static void
+gb_file_search_provider_constructed (GObject *object)
+{
+ GbFileSearchProvider *self = (GbFileSearchProvider *)object;
+ IdeContext *context;
+ IdeVcs *vcs;
+ GFile *workdir;
+
+ context = ide_object_get_context (IDE_OBJECT (self));
+ vcs = ide_context_get_vcs (context);
+ workdir = ide_vcs_get_working_directory (vcs);
+
+ self->index = g_object_new (GB_TYPE_FILE_SEARCH_INDEX,
+ "context", context,
+ "root-directory", workdir,
+ NULL);
+
+ gb_file_search_index_build_async (self->index,
+ NULL,
+ gb_file_search_provider_build_cb,
+ g_object_ref (self));
+
+ G_OBJECT_CLASS (gb_file_search_provider_parent_class)->constructed (object);
+}
+
+static void
gb_file_search_provider_finalize (GObject *object)
{
+ GbFileSearchProvider *self = (GbFileSearchProvider *)object;
+
+ g_clear_object (&self->index);
+
G_OBJECT_CLASS (gb_file_search_provider_parent_class)->finalize (object);
}
@@ -63,6 +164,7 @@ gb_file_search_provider_class_init (GbFileSearchProviderClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ object_class->constructed = gb_file_search_provider_constructed;
object_class->finalize = gb_file_search_provider_finalize;
}
@@ -76,4 +178,6 @@ 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;
}
diff --git a/plugins/file-search/gb-file-search-result.c b/plugins/file-search/gb-file-search-result.c
index e69de29..0fad4fc 100644
--- a/plugins/file-search/gb-file-search-result.c
+++ b/plugins/file-search/gb-file-search-result.c
@@ -0,0 +1,107 @@
+/* gb-file-search-result.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "gb-file-search-result.h"
+
+struct _GbFileSearchResult
+{
+ IdeSearchResult parent_instance;
+ gchar *path;
+};
+
+G_DEFINE_TYPE (GbFileSearchResult, gb_file_search_result, IDE_TYPE_SEARCH_RESULT)
+
+enum {
+ PROP_0,
+ PROP_PATH,
+ LAST_PROP
+};
+
+static GParamSpec *gParamSpecs [LAST_PROP];
+
+static void
+gb_file_search_result_finalize (GObject *object)
+{
+ GbFileSearchResult *self = (GbFileSearchResult *)object;
+
+ g_free (self->path);
+
+ G_OBJECT_CLASS (gb_file_search_result_parent_class)->finalize (object);
+}
+
+static void
+gb_file_search_result_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GbFileSearchResult *self = (GbFileSearchResult *)object;
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ g_value_set_string (value, self->path);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gb_file_search_result_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GbFileSearchResult *self = (GbFileSearchResult *)object;
+
+ switch (prop_id)
+ {
+ case PROP_PATH:
+ self->path = g_value_dup_string (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+gb_file_search_result_class_init (GbFileSearchResultClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gb_file_search_result_finalize;
+ object_class->get_property = gb_file_search_result_get_property;
+ object_class->set_property = gb_file_search_result_set_property;
+
+ gParamSpecs [PROP_PATH] =
+ g_param_spec_string ("path",
+ "Path",
+ "The relative path to the file.",
+ NULL,
+ (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_properties (object_class, LAST_PROP, gParamSpecs);
+}
+
+static void
+gb_file_search_result_init (GbFileSearchResult *self)
+{
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]