[gnome-builder] code-index: add accessory builder/executor objects



commit d420054ccd4f0589622c11add9b2d79e5055ce59
Author: Christian Hergert <chergert redhat com>
Date:   Fri Feb 1 20:32:44 2019 -0800

    code-index: add accessory builder/executor objects
    
    These can be used to simplify our indexing work a bit, going forward.

 src/plugins/code-index/gbp-code-index-builder.c  | 582 +++++++++++++++++++++++
 src/plugins/code-index/gbp-code-index-builder.h  |  46 ++
 src/plugins/code-index/gbp-code-index-executor.c | 288 +++++++++++
 src/plugins/code-index/gbp-code-index-executor.h |  43 ++
 src/plugins/code-index/gbp-code-index-plan.c     |   2 +-
 src/plugins/code-index/gbp-code-index-plan.h     |   2 +-
 src/plugins/code-index/meson.build               |   2 +
 7 files changed, 963 insertions(+), 2 deletions(-)
---
diff --git a/src/plugins/code-index/gbp-code-index-builder.c b/src/plugins/code-index/gbp-code-index-builder.c
new file mode 100644
index 000000000..fe79fe764
--- /dev/null
+++ b/src/plugins/code-index/gbp-code-index-builder.c
@@ -0,0 +1,582 @@
+/* gbp-code-index-builder.c
+ *
+ * Copyright 2019 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 "gbp-code-index-builder"
+
+#include "config.h"
+
+#include <dazzle.h>
+#include <libide-code.h>
+#include <libide-io.h>
+#include <libide-foundry.h>
+#include <libpeas/peas.h>
+
+#include "gbp-code-index-builder.h"
+#include "gbp-code-index-plan.h"
+
+struct _GbpCodeIndexBuilder
+{
+  IdeObject                parent_instance;
+  GFile                   *source_dir;
+  GFile                   *index_dir;
+  GPtrArray               *items;
+  IdePersistentMapBuilder *map;
+  DzlFuzzyIndexBuilder    *fuzzy;
+  guint                    next_file_id;
+  guint                    has_run : 1;
+};
+
+typedef struct
+{
+  guint n_active;
+  guint completed;
+} Run;
+
+G_DEFINE_TYPE (GbpCodeIndexBuilder, gbp_code_index_builder, IDE_TYPE_OBJECT)
+
+static void
+run_free (Run *state)
+{
+  g_slice_free (Run, state);
+}
+
+static void
+gbp_code_index_builder_finalize (GObject *object)
+{
+  GbpCodeIndexBuilder *self = (GbpCodeIndexBuilder *)object;
+
+  g_clear_object (&self->index_dir);
+  g_clear_object (&self->source_dir);
+  g_clear_pointer (&self->items, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (gbp_code_index_builder_parent_class)->finalize (object);
+}
+
+static void
+gbp_code_index_builder_class_init (GbpCodeIndexBuilderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gbp_code_index_builder_finalize;
+}
+
+static void
+gbp_code_index_builder_init (GbpCodeIndexBuilder *self)
+{
+  self->items = g_ptr_array_new_with_free_func ((GDestroyNotify)gbp_code_index_plan_item_free);
+  self->map = ide_persistent_map_builder_new ();
+  self->fuzzy = dzl_fuzzy_index_builder_new ();
+}
+
+static void
+gbp_code_index_builder_submit (GbpCodeIndexBuilder *self,
+                               GFile               *file,
+                               GPtrArray           *entries)
+{
+  g_autofree gchar *filename = NULL;
+  gchar num[16];
+  guint file_id;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_FILE (file));
+  g_assert (entries != NULL);
+
+  file_id = self->next_file_id;
+  self->next_file_id++;
+
+  /*
+   * 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.
+   *
+   * This can get called multiple times, but it's fine because we're just
+   * updating a GVariantDict until the file has been processed.
+   */
+  g_snprintf (num, sizeof (num), "%u", file_id);
+  filename = g_file_get_path (file);
+  dzl_fuzzy_index_builder_set_metadata_uint32 (self->fuzzy, filename, file_id);
+  dzl_fuzzy_index_builder_set_metadata_string (self->fuzzy, num, filename);
+
+  IDE_TRACE_MSG ("Adding %u entries for %s", entries->len, filename);
+
+  for (guint i = 0; i < entries->len; i++)
+    {
+      IdeCodeIndexEntry *entry = g_ptr_array_index (entries, i);
+      const gchar *key;
+      const gchar *name;
+      IdeSymbolKind kind;
+      IdeSymbolFlags flags;
+      guint begin_line;
+      guint begin_line_offset;
+
+      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 (self->map,
+                                           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 (self->fuzzy,
+                                        name,
+                                        g_variant_new ("(uuuuu)",
+                                                       file_id,
+                                                       begin_line,
+                                                       begin_line_offset,
+                                                       flags,
+                                                       kind),
+                                        0);
+    }
+}
+
+GbpCodeIndexBuilder *
+gbp_code_index_builder_new (GFile *source_dir,
+                            GFile *index_dir)
+{
+  GbpCodeIndexBuilder *self;
+
+  g_return_val_if_fail (G_IS_FILE (source_dir), NULL);
+  g_return_val_if_fail (G_IS_FILE (index_dir), NULL);
+
+  self = g_object_new (GBP_TYPE_CODE_INDEX_BUILDER, NULL);
+  self->source_dir = g_object_ref (source_dir);
+  self->index_dir = g_object_ref (index_dir);
+
+  return g_steal_pointer (&self);
+}
+
+void
+gbp_code_index_builder_add_item (GbpCodeIndexBuilder        *self,
+                                 const GbpCodeIndexPlanItem *item)
+{
+  g_return_if_fail (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_return_if_fail (self->has_run == FALSE);
+  g_return_if_fail (item != NULL);
+
+  g_ptr_array_add (self->items, gbp_code_index_plan_item_copy (item));
+}
+
+static void
+code_index_entries_collect_cb (GObject      *object,
+                               GAsyncResult *result,
+                               gpointer      user_data)
+{
+  IdeCodeIndexEntries *entries = (IdeCodeIndexEntries *)object;
+  GbpCodeIndexBuilder *self;
+  g_autoptr(GPtrArray) items = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  GFile *file;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CODE_INDEX_ENTRIES (entries));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  self = ide_task_get_source_object (task);
+  file = ide_task_get_task_data (task);
+
+  g_assert (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_FILE (file));
+
+  if (!(items = ide_code_index_entries_collect_finish (entries, result, &error)))
+    items = g_ptr_array_new_full (0, g_object_unref);
+
+  gbp_code_index_builder_submit (self, file, items);
+  ide_task_return_boolean (task, TRUE);
+}
+
+static void
+code_indexer_index_file_cb (GObject      *object,
+                            GAsyncResult *result,
+                            gpointer      user_data)
+{
+  IdeCodeIndexer *indexer = (IdeCodeIndexer *)object;
+  g_autoptr(IdeCodeIndexEntries) entries = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CODE_INDEXER (indexer));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!(entries = ide_code_indexer_index_file_finish (indexer, result, &error)))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_code_index_entries_collect_async (entries,
+                                          ide_task_get_cancellable (task),
+                                          code_index_entries_collect_cb,
+                                          g_object_ref (task));
+}
+
+static void
+gbp_code_index_builder_index_file_async (GbpCodeIndexBuilder *self,
+                                         GFile               *file,
+                                         IdeCodeIndexer      *indexer,
+                                         const gchar * const *build_flags,
+                                         GCancellable        *cancellable,
+                                         GAsyncReadyCallback  callback,
+                                         gpointer             user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+
+  g_assert (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_FILE (file));
+  g_assert (IDE_IS_CODE_INDEXER (indexer));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_code_index_builder_index_file_async);
+  ide_task_set_task_data (task, g_object_ref (file), g_object_unref);
+
+  ide_code_indexer_index_file_async (indexer,
+                                     file,
+                                     build_flags,
+                                     cancellable,
+                                     code_indexer_index_file_cb,
+                                     g_steal_pointer (&task));
+}
+
+static gboolean
+gbp_code_index_builder_index_file_finish (GbpCodeIndexBuilder  *self,
+                                          GAsyncResult         *result,
+                                          GError              **error)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_assert (IDE_IS_TASK (result));
+
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+gbp_code_index_builder_index_file_cb (GObject      *object,
+                                      GAsyncResult *result,
+                                      gpointer      user_data)
+{
+  GbpCodeIndexBuilder *self = (GbpCodeIndexBuilder *)object;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  Run *state;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  gbp_code_index_builder_index_file_finish (self, result, &error);
+
+  state = ide_task_get_task_data (task);
+  state->n_active--;
+  state->completed++;
+
+  if (state->n_active == 0)
+    ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_code_index_builder_aggregate_async (GbpCodeIndexBuilder *self,
+                                        GCancellable        *cancellable,
+                                        GAsyncReadyCallback  callback,
+                                        gpointer             user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+  g_autoptr(GHashTable) indexers = NULL;
+  PeasEngine *engine;
+  Run *state;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_code_index_builder_aggregate_async);
+
+  if (self->items->len == 0)
+    {
+      ide_task_return_boolean (task, TRUE);
+      IDE_EXIT;
+    }
+
+  state = g_slice_new0 (Run);
+  state->n_active = 1;
+  ide_task_set_task_data (task, state, run_free);
+
+  engine = peas_engine_get_default ();
+  indexers = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, g_object_unref);
+
+  /* We just queue up all of our indexer work up-front, and let the various
+   * backends manage their own queue-depth. That way, we don't waste time
+   * coordinating back and forth for async-but-serial processing.
+   */
+
+  for (guint i = 0; i < self->items->len; i++)
+    {
+      const GbpCodeIndexPlanItem *item = g_ptr_array_index (self->items, i);
+      const gchar *name = g_file_info_get_name (item->file_info);
+      g_autoptr(GFile) child = NULL;
+      IdeCodeIndexer *indexer;
+
+      if (name == NULL)
+        continue;
+
+      if (!(indexer = g_hash_table_lookup (indexers, item->indexer_module_name)))
+        {
+          PeasPluginInfo *plugin_info;
+
+          if (!(plugin_info = peas_engine_get_plugin_info (engine, item->indexer_module_name)))
+            continue;
+
+          indexer = (IdeCodeIndexer *)
+            peas_engine_create_extension (engine, plugin_info, IDE_TYPE_CODE_INDEXER,
+                                          "parent", self,
+                                          NULL);
+
+          if (indexer == NULL)
+            continue;
+
+          g_hash_table_insert (indexers, (gchar *)item->indexer_module_name, indexer);
+        }
+
+      state->n_active++;
+
+      child = g_file_get_child (self->source_dir, name);
+
+      gbp_code_index_builder_index_file_async (self,
+                                               child,
+                                               indexer,
+                                               (const gchar * const *)item->build_flags,
+                                               cancellable,
+                                               gbp_code_index_builder_index_file_cb,
+                                               g_object_ref (task));
+    }
+
+  state->n_active--;
+
+  g_ptr_array_remove_range (self->items, 0, self->items->len);
+
+  if (state->n_active == 0)
+    ide_task_return_boolean (task, TRUE);
+
+  IDE_EXIT;
+}
+
+static gboolean
+gbp_code_index_builder_aggregate_finish (GbpCodeIndexBuilder  *self,
+                                         GAsyncResult         *result,
+                                         GError              **error)
+{
+  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
+  g_return_val_if_fail (GBP_IS_CODE_INDEX_BUILDER (self), FALSE);
+  g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+gbp_code_index_builder_persist_write_fuzzy_cb (GObject      *object,
+                                               GAsyncResult *result,
+                                               gpointer      user_data)
+{
+  DzlFuzzyIndexBuilder *fuzzy = (DzlFuzzyIndexBuilder *)object;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (DZL_IS_FUZZY_INDEX_BUILDER (fuzzy));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!dzl_fuzzy_index_builder_write_finish (fuzzy, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_code_index_builder_persist_write_map_cb (GObject      *object,
+                                             GAsyncResult *result,
+                                             gpointer      user_data)
+{
+  IdePersistentMapBuilder *map = (IdePersistentMapBuilder *)object;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) file = NULL;
+  GbpCodeIndexBuilder *self;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_PERSISTENT_MAP_BUILDER (map));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!ide_persistent_map_builder_write_finish (map, result, &error))
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  self = ide_task_get_source_object (task);
+  file = g_file_get_child (self->index_dir, "SymbolNames");
+
+  dzl_fuzzy_index_builder_set_metadata_uint32 (self->fuzzy, "n_files", self->next_file_id);
+
+  dzl_fuzzy_index_builder_write_async (self->fuzzy,
+                                       file,
+                                       G_PRIORITY_DEFAULT,
+                                       ide_task_get_cancellable (task),
+                                       gbp_code_index_builder_persist_write_fuzzy_cb,
+                                       g_object_ref (task));
+}
+
+static void
+gbp_code_index_builder_persist_async (GbpCodeIndexBuilder *self,
+                                      GCancellable        *cancellable,
+                                      GAsyncReadyCallback  callback,
+                                      gpointer             user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+  g_autoptr(GFile) file = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_code_index_builder_persist_async);
+
+  file = g_file_get_child (self->index_dir, "SymbolKeys");
+
+  ide_persistent_map_builder_write_async (self->map,
+                                          file,
+                                          G_PRIORITY_DEFAULT,
+                                          cancellable,
+                                          gbp_code_index_builder_persist_write_map_cb,
+                                          g_steal_pointer (&task));
+}
+
+static gboolean
+gbp_code_index_builder_persist_finish (GbpCodeIndexBuilder  *self,
+                                       GAsyncResult         *result,
+                                       GError              **error)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_assert (IDE_IS_TASK (result));
+
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+gbp_code_index_builder_persist_cb (GObject      *object,
+                                   GAsyncResult *result,
+                                   gpointer      user_data)
+{
+  GbpCodeIndexBuilder *self = (GbpCodeIndexBuilder *)object;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!gbp_code_index_builder_persist_finish (self, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_task_return_boolean (task, TRUE);
+}
+
+static void
+gbp_code_index_builder_aggregate_cb (GObject      *object,
+                                     GAsyncResult *result,
+                                     gpointer      user_data)
+{
+  GbpCodeIndexBuilder *self = (GbpCodeIndexBuilder *)object;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!gbp_code_index_builder_aggregate_finish (self, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    gbp_code_index_builder_persist_async (self,
+                                          ide_task_get_cancellable (task),
+                                          gbp_code_index_builder_persist_cb,
+                                          g_object_ref (task));
+}
+
+void
+gbp_code_index_builder_run_async (GbpCodeIndexBuilder *self,
+                                  GCancellable        *cancellable,
+                                  GAsyncReadyCallback  callback,
+                                  gpointer             user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (GBP_IS_CODE_INDEX_BUILDER (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+  g_return_if_fail (self->has_run == FALSE);
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_code_index_builder_run_async);
+
+  self->has_run = TRUE;
+
+  gbp_code_index_builder_aggregate_async (self,
+                                          cancellable,
+                                          gbp_code_index_builder_aggregate_cb,
+                                          g_steal_pointer (&task));
+}
+
+gboolean
+gbp_code_index_builder_run_finish (GbpCodeIndexBuilder  *self,
+                                   GAsyncResult         *result,
+                                   GError              **error)
+{
+  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
+  g_return_val_if_fail (GBP_IS_CODE_INDEX_BUILDER (self), FALSE);
+  g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
diff --git a/src/plugins/code-index/gbp-code-index-builder.h b/src/plugins/code-index/gbp-code-index-builder.h
new file mode 100644
index 000000000..023c86472
--- /dev/null
+++ b/src/plugins/code-index/gbp-code-index-builder.h
@@ -0,0 +1,46 @@
+/* gbp-code-index-builder.h
+ *
+ * Copyright 2019 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 <libide-core.h>
+
+#include "gbp-code-index-plan.h"
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_CODE_INDEX_BUILDER (gbp_code_index_builder_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpCodeIndexBuilder, gbp_code_index_builder, GBP, CODE_INDEX_BUILDER, IdeObject)
+
+GbpCodeIndexBuilder *gbp_code_index_builder_new         (GFile                       *source_dir,
+                                                         GFile                       *index_dir);
+void                 gbp_code_index_builder_add_item    (GbpCodeIndexBuilder         *self,
+                                                         const GbpCodeIndexPlanItem  *item);
+void                 gbp_code_index_builder_run_async   (GbpCodeIndexBuilder         *self,
+                                                         GCancellable                *cancellable,
+                                                         GAsyncReadyCallback          callback,
+                                                         gpointer                     user_data);
+gboolean             gbp_code_index_builder_run_finish  (GbpCodeIndexBuilder         *self,
+                                                         GAsyncResult                *result,
+                                                         GError                     **error);
+
+
+G_END_DECLS
diff --git a/src/plugins/code-index/gbp-code-index-executor.c 
b/src/plugins/code-index/gbp-code-index-executor.c
new file mode 100644
index 000000000..79b735b1b
--- /dev/null
+++ b/src/plugins/code-index/gbp-code-index-executor.c
@@ -0,0 +1,288 @@
+/* gbp-code-index-executor.c
+ *
+ * Copyright 2019 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 "gbp-code-index-executor"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+#include <libide-code.h>
+#include <libide-foundry.h>
+#include <libide-threading.h>
+#include <libpeas/peas.h>
+
+#include "gbp-code-index-builder.h"
+#include "gbp-code-index-executor.h"
+
+struct _GbpCodeIndexExecutor
+{
+  IdeObject         parent_instance;
+  GbpCodeIndexPlan *plan;
+};
+
+typedef struct
+{
+  GbpCodeIndexPlan *plan;
+  IdeNotification  *notif;
+  GFile            *cachedir;
+  GFile            *workdir;
+  GPtrArray        *builders;
+  guint             pos;
+  guint64           num_ops;
+  guint64           num_completed;
+} Execute;
+
+G_DEFINE_TYPE (GbpCodeIndexExecutor, gbp_code_index_executor, IDE_TYPE_OBJECT)
+
+static void
+execute_free (Execute *exec)
+{
+  g_clear_object (&exec->plan);
+  g_clear_object (&exec->notif);
+  g_clear_object (&exec->cachedir);
+  g_clear_object (&exec->workdir);
+  g_clear_pointer (&exec->builders, g_ptr_array_unref);
+  g_slice_free (Execute, exec);
+}
+
+GbpCodeIndexExecutor *
+gbp_code_index_executor_new (GbpCodeIndexPlan *plan)
+{
+  GbpCodeIndexExecutor *self;
+
+  g_return_val_if_fail (GBP_IS_CODE_INDEX_PLAN (plan), NULL);
+
+  self = g_object_new (GBP_TYPE_CODE_INDEX_EXECUTOR, NULL);
+  self->plan = g_object_ref (plan);
+
+  return g_steal_pointer (&self);
+}
+
+static void
+gbp_code_index_executor_finalize (GObject *object)
+{
+  GbpCodeIndexExecutor *self = (GbpCodeIndexExecutor *)object;
+
+  g_clear_object (&self->plan);
+
+  G_OBJECT_CLASS (gbp_code_index_executor_parent_class)->finalize (object);
+}
+
+static void
+gbp_code_index_executor_class_init (GbpCodeIndexExecutorClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gbp_code_index_executor_finalize;
+}
+
+static void
+gbp_code_index_executor_init (GbpCodeIndexExecutor *self)
+{
+}
+
+static gboolean
+execute_count_ops_cb (GFile              *directory,
+                      GPtrArray          *plan_items,
+                      GbpCodeIndexReason  reason,
+                      gpointer            user_data)
+{
+  guint64 *count = user_data;
+  (*count)++;
+  return FALSE;
+}
+
+static guint64
+execute_count_ops (GbpCodeIndexPlan *plan)
+{
+  guint64 count = 0;
+  gbp_code_index_plan_foreach (plan, execute_count_ops_cb, &count);
+  return count;
+}
+
+static gboolean
+gbp_code_index_executor_collect_cb (GFile              *directory,
+                                    GPtrArray          *plan_items,
+                                    GbpCodeIndexReason  reason,
+                                    gpointer            user_data)
+{
+  g_autoptr(GbpCodeIndexBuilder) builder = NULL;
+  g_autoptr(GFile) index_dir = NULL;
+  g_autofree gchar *relative = NULL;
+  IdeTask *task = user_data;
+  GbpCodeIndexExecutor *self;
+  Execute *state;
+
+  g_assert (G_IS_FILE (directory));
+  g_assert (plan_items != NULL);
+  g_assert (IDE_IS_TASK (task));
+
+  self = ide_task_get_source_object (task);
+  state = ide_task_get_task_data (task);
+
+  relative = g_file_get_relative_path (state->workdir, directory);
+
+  if (relative == NULL)
+    index_dir = g_object_ref (state->cachedir);
+  else
+    index_dir = g_file_get_child (state->cachedir, relative);
+
+  if (reason == GBP_CODE_INDEX_REASON_REMOVE_INDEX)
+    {
+      g_autoptr(GFile) names = g_file_get_child (index_dir, "SymbolNames");
+      g_autoptr(GFile) keys = g_file_get_child (index_dir, "SymbolKeys");
+
+      g_file_delete_async (names, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
+      g_file_delete_async (keys, G_PRIORITY_DEFAULT, NULL, NULL, NULL);
+
+      state->num_completed++;
+
+      ide_notification_set_progress (state->notif,
+                                     (gdouble)state->num_completed / (gdouble)state->num_ops);
+
+      return FALSE;
+    }
+
+  builder = gbp_code_index_builder_new (directory, index_dir);
+  ide_object_append (IDE_OBJECT (self), IDE_OBJECT (builder));
+
+  for (guint i = 0; i < plan_items->len; i++)
+    gbp_code_index_builder_add_item (builder, g_ptr_array_index (plan_items, i));
+
+  g_ptr_array_add (state->builders, g_steal_pointer (&builder));
+
+  return FALSE;
+}
+
+static void
+gbp_code_index_executor_run_cb (GObject      *object,
+                                GAsyncResult *result,
+                                gpointer      user_data)
+{
+  GbpCodeIndexBuilder *builder = (GbpCodeIndexBuilder *)object;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  Execute *state;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_CODE_INDEX_BUILDER (builder));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  gbp_code_index_builder_run_finish (builder, result, &error);
+
+  state = ide_task_get_task_data (task);
+
+  state->pos++;
+  state->num_completed++;
+
+  ide_notification_set_progress (state->notif,
+                                 (gdouble)state->num_completed / (gdouble)state->num_ops);
+
+  if (state->pos >= state->builders->len)
+    {
+      ide_task_return_boolean (task, TRUE);
+      return;
+    }
+
+  gbp_code_index_builder_run_async (g_ptr_array_index (state->builders, state->pos),
+                                    ide_task_get_cancellable (task),
+                                    gbp_code_index_executor_run_cb,
+                                    g_object_ref (task));
+}
+
+void
+gbp_code_index_executor_execute_async (GbpCodeIndexExecutor *self,
+                                       IdeNotification      *notif,
+                                       GCancellable         *cancellable,
+                                       GAsyncReadyCallback   callback,
+                                       gpointer              user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+  g_autoptr(IdeContext) context = NULL;
+  Execute *state;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_MAIN_THREAD ());
+  g_return_if_fail (GBP_IS_CODE_INDEX_EXECUTOR (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_code_index_executor_execute_async);
+
+  if (!(context = ide_object_ref_context (IDE_OBJECT (self))))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_FAILED,
+                                 "Not connected to object tree");
+      IDE_EXIT;
+    }
+
+  state = g_slice_new0 (Execute);
+  state->plan = g_object_ref (self->plan);
+  state->notif = notif ? g_object_ref (notif) : ide_notification_new ();
+  state->num_ops = execute_count_ops (self->plan);
+  state->builders = g_ptr_array_new_with_free_func ((GDestroyNotify)ide_object_unref_and_destroy);
+  state->cachedir = ide_context_cache_file (context, "code-index", NULL);
+  state->workdir = ide_context_ref_workdir (context);
+  state->pos = 0;
+  ide_task_set_task_data (task, state, execute_free);
+
+  ide_notification_set_has_progress (state->notif, TRUE);
+  ide_notification_set_progress (state->notif, 0.0);
+  ide_notification_set_progress_is_imprecise (state->notif, FALSE);
+
+  gbp_code_index_plan_foreach (self->plan,
+                               gbp_code_index_executor_collect_cb,
+                               task);
+
+  if (state->builders->len == 0)
+    {
+      ide_task_return_boolean (task, TRUE);
+      IDE_EXIT;
+    }
+
+  gbp_code_index_builder_run_async (g_ptr_array_index (state->builders, 0),
+                                    cancellable,
+                                    gbp_code_index_executor_run_cb,
+                                    g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+gboolean
+gbp_code_index_executor_execute_finish (GbpCodeIndexExecutor  *self,
+                                        GAsyncResult          *result,
+                                        GError               **error)
+{
+  gboolean ret;
+
+  IDE_ENTRY;
+
+  g_return_val_if_fail (IDE_IS_MAIN_THREAD (), FALSE);
+  g_return_val_if_fail (GBP_IS_CODE_INDEX_EXECUTOR (self), FALSE);
+  g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+
+  ret = ide_task_propagate_boolean (IDE_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
diff --git a/src/plugins/code-index/gbp-code-index-executor.h 
b/src/plugins/code-index/gbp-code-index-executor.h
new file mode 100644
index 000000000..676d7b925
--- /dev/null
+++ b/src/plugins/code-index/gbp-code-index-executor.h
@@ -0,0 +1,43 @@
+/* gbp-code-index-executor.h
+ *
+ * Copyright 2019 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 <libide-core.h>
+
+#include "gbp-code-index-plan.h"
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_CODE_INDEX_EXECUTOR (gbp_code_index_executor_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpCodeIndexExecutor, gbp_code_index_executor, GBP, CODE_INDEX_EXECUTOR, IdeObject)
+
+GbpCodeIndexExecutor *gbp_code_index_executor_new            (GbpCodeIndexPlan      *plan);
+void                  gbp_code_index_executor_execute_async  (GbpCodeIndexExecutor  *self,
+                                                              IdeNotification       *notif,
+                                                              GCancellable          *cancellable,
+                                                              GAsyncReadyCallback    callback,
+                                                              gpointer               user_data);
+gboolean              gbp_code_index_executor_execute_finish (GbpCodeIndexExecutor  *self,
+                                                              GAsyncResult          *result,
+                                                              GError               **error);
+
+G_END_DECLS
diff --git a/src/plugins/code-index/gbp-code-index-plan.c b/src/plugins/code-index/gbp-code-index-plan.c
index 4193fa217..582a4bfd0 100644
--- a/src/plugins/code-index/gbp-code-index-plan.c
+++ b/src/plugins/code-index/gbp-code-index-plan.c
@@ -773,7 +773,7 @@ gbp_code_index_plan_load_flags_finish (GbpCodeIndexPlan  *self,
 }
 
 GbpCodeIndexPlanItem *
-gbp_code_index_plan_copy (const GbpCodeIndexPlanItem *item)
+gbp_code_index_plan_item_copy (const GbpCodeIndexPlanItem *item)
 {
   GbpCodeIndexPlanItem *ret;
 
diff --git a/src/plugins/code-index/gbp-code-index-plan.h b/src/plugins/code-index/gbp-code-index-plan.h
index be5753d34..e90337cba 100644
--- a/src/plugins/code-index/gbp-code-index-plan.h
+++ b/src/plugins/code-index/gbp-code-index-plan.h
@@ -84,7 +84,7 @@ gboolean              gbp_code_index_plan_load_flags_finish   (GbpCodeIndexPlan
 void                  gbp_code_index_plan_foreach             (GbpCodeIndexPlan            *self,
                                                                GbpCodeIndexPlanForeach      foreach_func,
                                                                gpointer                     foreach_data);
-GbpCodeIndexPlanItem *gbp_code_index_plan_copy                (const GbpCodeIndexPlanItem  *item);
+GbpCodeIndexPlanItem *gbp_code_index_plan_item_copy           (const GbpCodeIndexPlanItem  *item);
 void                  gbp_code_index_plan_item_free           (GbpCodeIndexPlanItem        *item);
 
 G_END_DECLS
diff --git a/src/plugins/code-index/meson.build b/src/plugins/code-index/meson.build
index dc6ae2b40..c9d886823 100644
--- a/src/plugins/code-index/meson.build
+++ b/src/plugins/code-index/meson.build
@@ -3,6 +3,8 @@ if get_option('plugin_code_index')
 plugins_sources += files([
   'code-index-plugin.c',
   'gbp-code-index-application-addin.c',
+  'gbp-code-index-builder.c',
+  'gbp-code-index-executor.c',
   'gbp-code-index-plan.c',
   'gbp-code-index-workbench-addin.c',
   'ide-code-index-builder.c',



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