[gnome-builder] code-index: avoid off main thread usage



commit bded6fb85a0c42860adc2a0749cfb2b8002c5c70
Author: Christian Hergert <chergert redhat com>
Date:   Thu Jan 18 06:40:27 2018 -0800

    code-index: avoid off main thread usage
    
    We wanted to remove the use of indexers called from threads and
    instead keep the communication between objects on the main
    thread (and let indexers do their own off-thread work if they
    so decide).

 src/plugins/code-index/ide-code-index-builder.c | 935 +++++++++++++++++++++++-
 1 file changed, 929 insertions(+), 6 deletions(-)
---
diff --git a/src/plugins/code-index/ide-code-index-builder.c b/src/plugins/code-index/ide-code-index-builder.c
index 1f42f0db5..954900928 100644
--- a/src/plugins/code-index/ide-code-index-builder.c
+++ b/src/plugins/code-index/ide-code-index-builder.c
@@ -19,15 +19,68 @@
 
 #define G_LOG_DOMAIN "ide-code-index-builder"
 
+#include <dazzle.h>
+#include <string.h>
+
 #include "ide-code-index-builder.h"
+#include "ide-persistent-map-builder.h"
 
 struct _IdeCodeIndexBuilder
 {
-  IdeObject parent;
+  IdeObject            parent;
   IdeCodeIndexService *service;
-  IdeCodeIndexIndex *index;
+  IdeCodeIndexIndex   *index;
 };
 
+#define BUILD_DATA_MAGIC      0x778124
+#define IS_BUILD_DATA(d)      ((d)->magic == BUILD_DATA_MAGIC)
+#define GET_CHANGES_MAGIC     0x912828
+#define IS_GET_CHANGES(d)     ((d)->magic == GET_CHANGES_MAGIC)
+#define FILE_INFO_MAGIC       0x112840
+#define IS_FILE_INFO(d)       ((d)->magic == FILE_INFO_MAGIC)
+#define INDEX_DIRECTORY_MAGIC 0x133801
+#define IS_INDEX_DIRECTORY(d) ((d)->magic == INDEX_DIRECTORY_MAGIC)
+
+typedef struct
+{
+  guint           magic;
+  GFile          *data_dir;
+  GFile          *index_dir;
+  GFile          *building_data_dir;
+  GFile          *building_index_dir;
+  IdeBuildSystem *build_system;
+  GPtrArray      *changes;
+} BuildData;
+
+typedef struct
+{
+  guint   magic;
+  GFile  *data_dir;
+  GFile  *index_dir;
+  IdeVcs *vcs;
+  GQueue  directories;
+  guint   recursive : 1;
+} GetChangesData;
+
+typedef struct
+{
+  guint      magic;
+  GFile     *directory;
+  gchar     *name;
+  GTimeVal   mtime;
+  GFileType  file_type;
+} FileInfo;
+
+typedef struct
+{
+  guint                    magic;
+  IdePersistentMapBuilder *map;
+  DzlFuzzyIndexBuilder    *fuzzy;
+  GFile                   *index_dir;
+  guint                    n_active;
+  guint                    n_files;
+} IndexDirectoryData;
+
 enum {
   PROP_0,
   PROP_INDEX,
@@ -35,10 +88,80 @@ enum {
   N_PROPS
 };
 
+static void build_tick (GTask *task);
+
 G_DEFINE_TYPE (IdeCodeIndexBuilder, ide_code_index_builder, IDE_TYPE_OBJECT)
 
 static GParamSpec *properties [N_PROPS];
 
+static void
+build_data_free (BuildData *self)
+{
+  g_clear_object (&self->build_system);
+  g_clear_object (&self->data_dir);
+  g_clear_object (&self->index_dir);
+  g_clear_object (&self->building_data_dir);
+  g_clear_object (&self->building_index_dir);
+  g_clear_pointer (&self->changes, g_ptr_array_unref);
+  g_slice_free (BuildData, self);
+}
+
+static void
+get_changes_data_free (GetChangesData *self)
+{
+
+  g_clear_object (&self->data_dir);
+  g_clear_object (&self->index_dir);
+  g_clear_object (&self->vcs);
+  g_queue_foreach (&self->directories, (GFunc)g_object_unref, NULL);
+  g_queue_clear (&self->directories);
+  g_slice_free (GetChangesData, self);
+}
+
+static void
+file_info_free (FileInfo *file_info)
+{
+  g_clear_pointer (&file_info->name, g_free);
+  g_clear_object (&file_info->directory);
+  g_slice_free (FileInfo, file_info);
+}
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (FileInfo, file_info_free)
+
+static void
+index_directory_data_free (IndexDirectoryData *self)
+{
+  g_assert (self != NULL);
+  g_assert (self->n_active == 0);
+
+  g_clear_object (&self->map);
+  g_clear_object (&self->fuzzy);
+  g_clear_object (&self->index_dir);
+  g_slice_free (IndexDirectoryData, self);
+}
+
+static gint
+timeval_compare (const GTimeVal *a,
+                 const GTimeVal *b)
+{
+  if (memcmp (a, b, sizeof *a) == 0)
+    return 0;
+  else if (a->tv_sec < b->tv_sec || (a->tv_sec == b->tv_sec && a->tv_usec < b->tv_usec))
+    return -1;
+  else
+    return 1;
+}
+
+static void
+maybe_log_error (const GError *error)
+{
+  if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED) ||
+      g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED))
+    return;
+
+  g_warning ("%s", error->message);
+}
+
 static void
 ide_code_index_builder_dispose (GObject *object)
 {
@@ -143,12 +266,782 @@ ide_code_index_builder_new (IdeContext          *context,
                        NULL);
 }
 
-void
-ide_code_index_builder_drop_caches (IdeCodeIndexBuilder *self)
+static GFile *
+get_index_dir (GFile *index_root,
+               GFile *data_root,
+               GFile *directory)
 {
-  g_return_if_fail (IDE_IS_CODE_INDEX_BUILDER (self));
+  g_autofree gchar *relative = NULL;
+
+  g_assert (G_IS_FILE (index_root));
+  g_assert (G_IS_FILE (data_root));
+  g_assert (G_IS_FILE (directory));
+  g_assert (g_file_equal (data_root, directory) ||
+            g_file_has_prefix (directory, data_root));
+
+  relative = g_file_get_relative_path (data_root, directory);
+
+  if (relative != NULL)
+    return g_file_get_child (index_root, relative);
+  else
+    return g_object_ref (index_root);
+}
+
+static void
+remove_indexes_in_dir (GFile        *index_dir,
+                       GCancellable *cancellable)
+{
+  g_autoptr(GFile) keys = NULL;
+  g_autoptr(GFile) names = NULL;
+
+  g_assert (G_IS_FILE (index_dir));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  keys = g_file_get_child (index_dir, "SymbolKeys");
+  names = g_file_get_child (index_dir, "SymbolNames");
+
+  g_file_delete (keys, cancellable, NULL);
+  g_file_delete (names, cancellable, NULL);
+}
+
+static gboolean
+directory_needs_update (GFile        *index_dir,
+                        GFile        *directory,
+                        GQueue       *file_infos,
+                        GCancellable *cancellable)
+{
+  g_autoptr(GFile) names_file = NULL;
+  g_autoptr(GFileInfo) names_info = NULL;
+  g_autoptr(DzlFuzzyIndex) index = NULL;
+  GTimeVal mtime;
+  guint n_files;
+
+  g_assert (G_IS_FILE (index_dir));
+  g_assert (G_IS_FILE (directory));
+  g_assert (file_infos != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  names_file = g_file_get_child (index_dir, "SymbolNames");
+  names_info = g_file_query_info (names_file,
+                                  G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                                  G_FILE_QUERY_INFO_NONE,
+                                  cancellable,
+                                  NULL);
+  if (names_info == NULL)
+    return TRUE;
+
+  g_file_info_get_modification_time (names_info, &mtime);
+
+  /* If the mtime of a file is newer than the index, it needs indexing */
+  for (const GList *iter = file_infos->head; iter != NULL; iter = iter->next)
+    {
+      const FileInfo *info = iter->data;
+
+      if (timeval_compare (&info->mtime, &mtime) > 0)
+        return TRUE;
+    }
+
+  /* Load the SymbolNames index for this directory */
+  index = dzl_fuzzy_index_new ();
+  if (!dzl_fuzzy_index_load_file (index, names_file, cancellable, NULL))
+    return TRUE;
+
+  /* If the file number count is off, it needs indexing */
+  n_files = dzl_fuzzy_index_get_metadata_uint32 (index, "n_files");
+  if (n_files != file_infos->length)
+    return TRUE;
+
+  /* If any file names are missing from metadata, it needs indexing */
+  for (const GList *iter = file_infos->head; iter != NULL; iter = iter->next)
+    {
+      const FileInfo *info = iter->data;
+      g_autoptr(GFile) file = g_file_get_child (info->directory, info->name);
+      g_autofree gchar *path = g_file_get_path (file);
 
-  g_warning ("TODO: drop caches");
+      if (!dzl_fuzzy_index_get_metadata_uint32 (index, path))
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+filter_ignored (IdeVcs *vcs,
+                GQueue *file_infos)
+{
+  g_assert (IDE_IS_VCS (vcs));
+  g_assert (file_infos != NULL);
+
+  for (GList *iter = file_infos->head; iter; iter = iter->next)
+    {
+      FileInfo *info;
+      GFile *file;
+      gboolean ignore;
+
+    again:
+      info = iter->data;
+
+      g_assert (IS_FILE_INFO (info));
+
+      file = g_file_get_child (info->directory, info->name);
+      ignore = ide_vcs_is_ignored (vcs, file, NULL);
+      g_clear_object (&file);
+
+      if (ignore)
+        {
+          GList *tmp = iter->next;
+          g_queue_delete_link (file_infos, iter);
+          if (tmp == NULL)
+            break;
+          iter = tmp;
+          goto again;
+        }
+    }
+}
+
+static void
+find_all_files_typed (GFile        *root,
+                      GFileType     req_file_type,
+                      gboolean      recursive,
+                      GCancellable *cancellable,
+                      GFunc         func,
+                      gpointer      user_data)
+{
+  g_autoptr(GFileEnumerator) enumerator = NULL;
+
+  g_assert (G_IS_FILE (root));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_assert (func != NULL);
+
+  enumerator = g_file_enumerate_children (root,
+                                          G_FILE_ATTRIBUTE_STANDARD_NAME","
+                                          G_FILE_ATTRIBUTE_STANDARD_TYPE","
+                                          G_FILE_ATTRIBUTE_TIME_MODIFIED,
+                                          G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
+                                          cancellable,
+                                          NULL);
+  if (enumerator == NULL)
+    return;
+
+  for (;;)
+    {
+      g_autoptr(GFileInfo) info = NULL;
+      GFileType file_type;
+
+      if (g_cancellable_is_cancelled (cancellable))
+        break;
+
+      if (!(info = g_file_enumerator_next_file (enumerator, cancellable, NULL)))
+        break;
+
+      file_type = g_file_info_get_file_type (info);
+
+      if (file_type == req_file_type)
+        {
+          FileInfo *fi;
+
+          fi = g_slice_new0 (FileInfo);
+          fi->magic = FILE_INFO_MAGIC;
+          fi->directory = g_object_ref (root);
+          fi->name = g_strdup (g_file_info_get_name (info));
+          fi->file_type = g_file_info_get_file_type (info);
+          g_file_info_get_modification_time (info, &fi->mtime);
+
+          func (g_steal_pointer (&fi), user_data);
+        }
+
+      if (recursive && file_type == G_FILE_TYPE_DIRECTORY)
+        {
+          g_autoptr(GFile) child = g_file_enumerator_get_child (enumerator, info);
+
+          find_all_files_typed (child,
+                                req_file_type,
+                                TRUE,
+                                cancellable,
+                                func,
+                                user_data);
+        }
+    }
+
+  g_file_enumerator_close (enumerator, NULL, NULL);
+}
+
+static void
+get_changes_collect_dirs_cb (gpointer data,
+                             gpointer user_data)
+{
+  g_autoptr(FileInfo) fi = data;
+  GQueue *queue = user_data;
+
+  g_assert (fi != NULL);
+  g_assert (G_IS_FILE (fi->directory));
+  g_assert (fi->name != NULL);
+  g_assert (queue != NULL);
+
+  g_queue_push_tail (queue, g_file_get_child (fi->directory, fi->name));
+}
+
+static void
+get_changes_collect_files_cb (gpointer data,
+                              gpointer user_data)
+{
+  g_autoptr(FileInfo) fi = data;
+  GQueue *queue = user_data;
+
+  g_assert (fi != NULL);
+  g_assert (G_IS_FILE (fi->directory));
+  g_assert (fi->name != NULL);
+  g_assert (queue != NULL);
+
+  g_queue_push_tail (queue, g_steal_pointer (&fi));
+}
+
+static void
+get_changes_worker (GTask        *task,
+                    gpointer      source_object,
+                    gpointer      task_data,
+                    GCancellable *cancellable)
+{
+  g_autoptr(GPtrArray) to_update = NULL;
+  GetChangesData *gcd = task_data;
+
+  g_assert (!IDE_IS_MAIN_THREAD ());
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_CODE_INDEX_BUILDER (source_object));
+  g_assert (gcd != NULL);
+  g_assert (IS_GET_CHANGES (gcd));
+  g_assert (G_IS_FILE (gcd->data_dir));
+  g_assert (G_IS_FILE (gcd->index_dir));
+  g_assert (IDE_IS_VCS (gcd->vcs));
+
+  if (g_task_return_error_if_cancelled (task))
+    return;
+
+  /*
+   * If we are recursive, collect all the directories we need to look
+   * at to locate changes.
+   */
+  if (gcd->recursive)
+    find_all_files_typed (gcd->data_dir,
+                          G_FILE_TYPE_DIRECTORY,
+                          TRUE,
+                          cancellable,
+                          get_changes_collect_dirs_cb,
+                          &gcd->directories);
+
+  /*
+   * We'll keep track of all the directories which contain data
+   * that is invalid and needs updating.
+   */
+  to_update = g_ptr_array_new_with_free_func (g_object_unref);
+
+  /*
+   * Process directories to check for changes, while ensuring we have not
+   * been asynchronously cancelled.
+   */
+  while (!g_task_return_error_if_cancelled (task) &&
+         !g_queue_is_empty (&gcd->directories))
+    {
+      g_autofree gchar *relative = NULL;
+      g_autoptr(GFile) dir = g_queue_pop_head (&gcd->directories);
+      g_autoptr(GFile) index_dir = NULL;
+      g_auto(GQueue) files = G_QUEUE_INIT;
+
+      g_assert (G_IS_FILE (dir));
+
+      find_all_files_typed (dir,
+                            G_FILE_TYPE_REGULAR,
+                            FALSE,
+                            cancellable,
+                            get_changes_collect_files_cb,
+                            &files);
+
+      filter_ignored (gcd->vcs, &files);
+
+      if (!(relative = g_file_get_relative_path (gcd->index_dir, dir)))
+        index_dir = g_object_ref (gcd->index_dir);
+      else
+        index_dir = g_file_get_child (gcd->index_dir, relative);
+
+      if (files.length == 0)
+        remove_indexes_in_dir (index_dir, cancellable);
+      else if (directory_needs_update (index_dir, dir, &files, cancellable))
+        g_ptr_array_add (to_update, g_steal_pointer (&dir));
+
+      g_queue_foreach (&files, (GFunc)file_info_free, NULL);
+    }
+
+  g_assert (gcd->directories.length == 0);
+  g_assert (IDE_IS_VCS (gcd->vcs));
+  g_assert (G_IS_FILE (gcd->data_dir));
+  g_assert (G_IS_FILE (gcd->index_dir));
+
+  g_task_return_pointer (task,
+                         g_steal_pointer (&to_update),
+                         (GDestroyNotify) g_ptr_array_unref);
+}
+
+/*
+ * get_changes_async:
+ * @self: an #IdeCodeIndexBuilder
+ * @directory: a #GFile
+ * @recursive: if the directories should be recursively scanned
+ * @cancellable: a cancellable or %NULL
+ * @callback: callback to execute upon completion
+ * @user_data: closure data for @callback
+ *
+ * This asynchronously looks for all of the directories starting from
+ * @directoriy (if recursive is set) that contain changes to their
+ * existing index.
+ */
+static void
+get_changes_async (IdeCodeIndexBuilder *self,
+                   GFile               *data_dir,
+                   GFile               *index_dir,
+                   gboolean             recursive,
+                   GCancellable        *cancellable,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  GetChangesData *gcd;
+  IdeContext *context;
+  IdeVcs *vcs;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_FILE (data_dir));
+  g_assert (G_IS_FILE (index_dir));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  vcs = ide_context_get_vcs (context);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, get_changes_async);
+  g_task_set_priority (task, G_PRIORITY_LOW);
+
+  gcd = g_slice_new0 (GetChangesData);
+  gcd->magic = GET_CHANGES_MAGIC;
+  gcd->data_dir = g_object_ref (data_dir);
+  gcd->index_dir = g_object_ref (index_dir);
+  gcd->recursive = !!recursive;
+  gcd->vcs = g_object_ref (vcs);
+  g_task_set_task_data (task, gcd, (GDestroyNotify)get_changes_data_free);
+
+  g_queue_push_head (&gcd->directories, g_object_ref (data_dir));
+
+  ide_thread_pool_push_task (IDE_THREAD_POOL_INDEXER, task, get_changes_worker);
+}
+
+static GPtrArray *
+get_changes_finish (IdeCodeIndexBuilder  *self,
+                    GAsyncResult         *result,
+                    GError              **error)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_TASK (result));
+
+  return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static void
+add_entries_to_index (IdeCodeIndexEntries     *entries,
+                      guint32                  file_id,
+                      IdePersistentMapBuilder *map_builder,
+                      DzlFuzzyIndexBuilder    *fuzzy_builder)
+{
+  g_autofree gchar *filename = NULL;
+  g_autoptr(GFile) file = NULL;
+  gchar num[16];
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CODE_INDEX_ENTRIES (entries));
+  g_assert (file_id > 0);
+  g_assert (IDE_IS_PERSISTENT_MAP_BUILDER (map_builder));
+  g_assert (DZL_IS_FUZZY_INDEX_BUILDER (fuzzy_builder));
+
+  file = ide_code_index_entries_get_file (entries);
+  g_assert (G_IS_FILE (file));
+
+  /*
+   * Storing file_name:id and id:file_name into index, file_name:id will be
+   * used to check whether a file is there in index or not.
+   */
+  g_snprintf (num, sizeof (num), "%u", file_id);
+  filename = g_file_get_path (file);
+  dzl_fuzzy_index_builder_set_metadata_uint32 (fuzzy_builder, filename, file_id);
+  dzl_fuzzy_index_builder_set_metadata_string (fuzzy_builder, num, filename);
+
+  for (;;)
+    {
+      g_autoptr(IdeCodeIndexEntry) entry = NULL;
+      const gchar *key;
+      const gchar *name;
+      IdeSymbolKind kind;
+      IdeSymbolFlags flags;
+      guint begin_line;
+      guint begin_line_offset;
+
+      if (!(entry = ide_code_index_entries_get_next_entry (entries)))
+        break;
+
+      key = ide_code_index_entry_get_key (entry);
+      name  = ide_code_index_entry_get_name (entry);
+      kind  = ide_code_index_entry_get_kind (entry);
+      flags  = ide_code_index_entry_get_flags (entry);
+
+      ide_code_index_entry_get_range (entry,
+                                      &begin_line,
+                                      &begin_line_offset,
+                                      NULL,
+                                      NULL);
+
+      /* In our index lines and offsets are 1-based */
+
+      if (key != NULL)
+        ide_persistent_map_builder_insert (map_builder,
+                                           key,
+                                           g_variant_new ("(uuuu)",
+                                                          file_id,
+                                                          begin_line,
+                                                          begin_line_offset,
+                                                          flags),
+                                           !!(flags & IDE_SYMBOL_FLAGS_IS_DEFINITION));
+
+      if (name != NULL)
+        dzl_fuzzy_index_builder_insert (fuzzy_builder,
+                                        name,
+                                        g_variant_new ("(uuuuu)",
+                                                       file_id,
+                                                       begin_line,
+                                                       begin_line_offset,
+                                                       flags,
+                                                       kind),
+                                        0);
+    }
+}
+
+static void
+index_directory_worker (GTask        *task,
+                        gpointer      source_object,
+                        gpointer      task_data,
+                        GCancellable *cancellable)
+{
+  IndexDirectoryData *idd = task_data;
+  g_autoptr(GFile) names = NULL;
+  g_autoptr(GFile) keys = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (G_IS_TASK (task));
+  g_assert (IDE_IS_CODE_INDEX_BUILDER (source_object));
+  g_assert (idd != NULL);
+  g_assert (IS_INDEX_DIRECTORY (idd));
+  g_assert (G_IS_FILE (idd->index_dir));
+  g_assert (DZL_IS_FUZZY_INDEX_BUILDER (idd->fuzzy));
+  g_assert (IDE_IS_PERSISTENT_MAP_BUILDER (idd->map));
+
+  keys = g_file_get_child (idd->index_dir, "SymbolKeys");
+  names = g_file_get_child (idd->index_dir, "SymbolNames");
+
+  /* Ignore result, as it will set @error if index_dir exists */
+  g_file_make_directory_with_parents (idd->index_dir, cancellable, NULL);
+
+  if (!ide_persistent_map_builder_write (idd->map, keys, 0, cancellable, &error) ||
+      !dzl_fuzzy_index_builder_write (idd->fuzzy, names, 0, cancellable, &error))
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    g_task_return_boolean (task, TRUE);
+}
+
+static void
+index_directory_index_file_cb (GObject      *object,
+                               GAsyncResult *result,
+                               gpointer      user_data)
+{
+  IdeCodeIndexer *indexer = (IdeCodeIndexer *)object;
+  g_autoptr(IdeCodeIndexEntries) entries = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+  IndexDirectoryData *idd;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CODE_INDEXER (indexer));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  idd = g_task_get_task_data (task);
+  g_assert (idd != NULL);
+  g_assert (IS_INDEX_DIRECTORY (idd));
+  g_assert (G_IS_FILE (idd->index_dir));
+  g_assert (DZL_IS_FUZZY_INDEX_BUILDER (idd->fuzzy));
+  g_assert (IDE_IS_PERSISTENT_MAP_BUILDER (idd->map));
+
+  entries = ide_code_indexer_index_file_finish (indexer, result, &error);
+
+  if (entries == NULL)
+    maybe_log_error (error);
+  else
+    add_entries_to_index (entries, ++idd->n_files, idd->map, idd->fuzzy);
+
+  idd->n_active--;
+
+  if (idd->n_active == 0)
+    {
+      dzl_fuzzy_index_builder_set_metadata_uint32 (idd->fuzzy, "n_files", idd->n_files);
+      g_task_run_in_thread (task, index_directory_worker);
+    }
+}
+
+
+static void
+index_directory_async (IdeCodeIndexBuilder *self,
+                       GFile               *data_dir,
+                       GFile               *index_dir,
+                       GHashTable          *build_flags,
+                       GCancellable        *cancellable,
+                       GAsyncReadyCallback  callback,
+                       gpointer             user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  IndexDirectoryData *idd;
+  GHashTableIter iter;
+  gpointer k, v;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_FILE (data_dir));
+  g_assert (G_IS_FILE (index_dir));
+  g_assert (build_flags != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, index_directory_async);
+  g_task_set_priority (task, G_PRIORITY_LOW);
+
+  idd = g_slice_new0 (IndexDirectoryData);
+  idd->magic = INDEX_DIRECTORY_MAGIC;
+  idd->index_dir = g_object_ref (index_dir);
+  idd->fuzzy = dzl_fuzzy_index_builder_new ();
+  idd->map = ide_persistent_map_builder_new ();
+  g_task_set_task_data (task, idd, (GDestroyNotify)index_directory_data_free);
+
+  g_hash_table_iter_init (&iter, build_flags);
+
+  while (g_hash_table_iter_next (&iter, &k, &v))
+    {
+      IdeFile *file = k;
+      const gchar * const *file_flags = v;
+      const gchar *path = ide_file_get_path (file);
+      GFile *gfile = ide_file_get_file (file);
+      IdeCodeIndexer *indexer;
+
+      g_assert (IDE_IS_FILE (file));
+      g_assert (G_IS_FILE (gfile));
+      g_assert (path != NULL);
+      g_assert (IDE_IS_CODE_INDEX_SERVICE (self->service));
+
+      if ((indexer = ide_code_index_service_get_code_indexer (self->service, path)))
+        {
+          idd->n_active++;
+          ide_code_indexer_index_file_async (indexer,
+                                             gfile,
+                                             file_flags,
+                                             cancellable,
+                                             index_directory_index_file_cb,
+                                             g_object_ref (task));
+        }
+    }
+
+  if (idd->n_active == 0)
+    g_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+index_directory_finish (IdeCodeIndexBuilder  *self,
+                        GAsyncResult         *result,
+                        GError              **error)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_TASK (result));
+  g_assert (g_task_is_valid (G_TASK (result), self));
+
+  return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+build_index_directory_cb (GObject      *object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+  IdeCodeIndexBuilder *self = (IdeCodeIndexBuilder *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  BuildData *bd;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  if (g_task_return_error_if_cancelled (task))
+    return;
+
+  bd = g_task_get_task_data (task);
+  g_assert (bd != NULL);
+  g_assert (IS_BUILD_DATA (bd));
+  g_assert (G_IS_FILE (bd->building_data_dir));
+  g_assert (G_IS_FILE (bd->building_index_dir));
+
+  if (!index_directory_finish (self, result, &error))
+    maybe_log_error (error);
+  else if (self->index != NULL)
+    ide_code_index_index_load (self->index,
+                               bd->building_index_dir,
+                               bd->building_data_dir,
+                               NULL, NULL);
+
+  build_tick (task);
+}
+
+static void
+build_get_build_flags_cb (GObject      *object,
+                          GAsyncResult *result,
+                          gpointer      user_data)
+{
+  IdeBuildSystem *build_system = (IdeBuildSystem *)object;
+  g_autoptr(GTask) task = user_data;
+  g_autoptr(GHashTable) flags = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) data_dir = NULL;
+  g_autoptr(GFile) index_dir = NULL;
+  g_autofree gchar *relative = NULL;
+  IdeCodeIndexBuilder *self;
+  GCancellable *cancellable;
+  BuildData *bd;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_BUILD_SYSTEM (build_system));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  bd = g_task_get_task_data (task);
+  g_assert (bd != NULL);
+  g_assert (G_IS_FILE (bd->data_dir));
+  g_assert (G_IS_FILE (bd->index_dir));
+  g_assert (bd->changes != NULL);
+  g_assert (bd->changes->len > 0);
+  g_assert (IDE_IS_BUILD_SYSTEM (bd->build_system));
+
+  data_dir = g_object_ref (g_ptr_array_index (bd->changes, bd->changes->len - 1));
+  g_ptr_array_remove_index (bd->changes, bd->changes->len - 1);
+  g_assert (G_IS_FILE (data_dir));
+
+  if (!(flags = ide_build_system_get_build_flags_for_dir_finish (build_system, result, &error)))
+    {
+      maybe_log_error (error);
+      build_tick (task);
+      return;
+    }
+
+  self = g_task_get_source_object (task);
+  g_assert (IDE_IS_CODE_INDEX_BUILDER (self));
+
+  cancellable = g_task_get_cancellable (task);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  index_dir = get_index_dir (bd->index_dir, bd->data_dir, data_dir);
+  g_assert (G_IS_FILE (index_dir));
+
+  g_set_object (&bd->building_index_dir, index_dir);
+  g_set_object (&bd->building_data_dir, data_dir);
+
+  g_assert (G_IS_FILE (bd->building_index_dir));
+  g_assert (G_IS_FILE (bd->building_data_dir));
+
+  {
+    g_autofree gchar *path = g_file_get_path (data_dir);
+    g_debug ("Indexing code in directory %s", path);
+  }
+
+  index_directory_async (self,
+                         data_dir,
+                         index_dir,
+                         flags,
+                         cancellable,
+                         build_index_directory_cb,
+                         g_steal_pointer (&task));
+}
+
+static void
+build_tick (GTask *task)
+{
+  GCancellable *cancellable;
+  BuildData *bd;
+  GFile *data_dir;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (G_IS_TASK (task));
+
+  if (g_task_return_error_if_cancelled (task))
+    return;
+
+  bd = g_task_get_task_data (task);
+  g_assert (bd != NULL);
+  g_assert (IS_BUILD_DATA (bd));
+  g_assert (G_IS_FILE (bd->data_dir));
+  g_assert (G_IS_FILE (bd->index_dir));
+  g_assert (IDE_IS_BUILD_SYSTEM (bd->build_system));
+
+  if (bd->changes->len == 0)
+    {
+      g_task_return_boolean (task, TRUE);
+      return;
+    }
+
+  cancellable = g_task_get_cancellable (task);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  data_dir = g_ptr_array_index (bd->changes, bd->changes->len - 1);
+  g_assert (G_IS_FILE (data_dir));
+
+  ide_build_system_get_build_flags_for_dir_async (bd->build_system,
+                                                  data_dir,
+                                                  cancellable,
+                                                  build_get_build_flags_cb,
+                                                  g_object_ref (task));
+}
+
+static void
+build_get_changes_cb (GObject      *object,
+                      GAsyncResult *result,
+                      gpointer      user_data)
+{
+  IdeCodeIndexBuilder *self = (IdeCodeIndexBuilder *)object;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GTask) task = user_data;
+  BuildData *bd;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (G_IS_TASK (task));
+
+  bd = g_task_get_task_data (task);
+  g_assert (bd != NULL);
+  g_assert (G_IS_FILE (bd->data_dir));
+  g_assert (G_IS_FILE (bd->index_dir));
+  g_assert (bd->changes == NULL);
+  g_assert (IDE_IS_BUILD_SYSTEM (bd->build_system));
+
+  bd->changes = get_changes_finish (self, result, &error);
+
+  if (bd->changes == NULL)
+    g_task_return_error (task, g_steal_pointer (&error));
+  else
+    build_tick (task);
 }
 
 void
@@ -159,16 +1052,45 @@ ide_code_index_builder_build_async (IdeCodeIndexBuilder *self,
                                     GAsyncReadyCallback  callback,
                                     gpointer             user_data)
 {
+  g_autofree gchar *relative = NULL;
   g_autoptr(GTask) task = NULL;
+  g_autoptr(GFile) index_dir = NULL;
+  IdeBuildSystem *build_system;
+  IdeContext *context;
+  BuildData *bd;
+  IdeVcs *vcs;
+  GFile *workdir;
 
+  g_return_if_fail (IDE_IS_MAIN_THREAD ());
   g_return_if_fail (IDE_IS_CODE_INDEX_BUILDER (self));
   g_return_if_fail (G_IS_FILE (directory));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  context = ide_object_get_context (IDE_OBJECT (self));
+  build_system = ide_context_get_build_system (context);
+  vcs = ide_context_get_vcs (context);
+  workdir = ide_vcs_get_working_directory (vcs);
+  relative = g_file_get_relative_path (workdir, directory);
+  index_dir = ide_context_cache_file (context, "code-index", relative, NULL);
+
   task = g_task_new (self, cancellable, callback, user_data);
   g_task_set_source_tag (task, ide_code_index_builder_build_async);
   g_task_set_priority (task, G_PRIORITY_LOW);
 
+  bd = g_slice_new0 (BuildData);
+  bd->magic = BUILD_DATA_MAGIC;
+  bd->data_dir = g_object_ref (directory);
+  bd->index_dir = g_steal_pointer (&index_dir);
+  bd->build_system = g_object_ref (build_system);
+  g_task_set_task_data (task, bd, (GDestroyNotify)build_data_free);
+
+  get_changes_async (self,
+                     bd->data_dir,
+                     bd->index_dir,
+                     recursive,
+                     cancellable,
+                     build_get_changes_cb,
+                     g_steal_pointer (&task));
 }
 
 gboolean
@@ -176,6 +1098,7 @@ ide_code_index_builder_build_finish (IdeCodeIndexBuilder  *self,
                                      GAsyncResult         *result,
                                      GError              **error)
 {
+  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
   g_return_val_if_fail (IDE_IS_CODE_INDEX_BUILDER (self), FALSE);
   g_return_val_if_fail (G_IS_TASK (result), FALSE);
 


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