[gnome-builder: 72/139] ctags: port to new libide-core



commit d48afaa2d60b08c0a3959cb9e892fd9f70c769ca
Author: Christian Hergert <chergert redhat com>
Date:   Wed Jan 9 17:13:39 2019 -0800

    ctags: port to new libide-core
    
    This alters the ctags plugin design a bit to integrate better with the new
    libide-core design. We no longer use a service, but simply add an object
    onto the IdeContext object graph. We also use IdeNotification to propagate
    information about the process to the UI which is monitoring notifications.

 src/plugins/ctags/ctags-plugin.c                   |  34 ++-
 src/plugins/ctags/ctags.gresource.xml              |   2 +-
 src/plugins/ctags/ctags.plugin                     |  15 +-
 src/plugins/ctags/gbp-ctags-workbench-addin.c      | 183 ++++++++++++
 src/plugins/ctags/gbp-ctags-workbench-addin.h      |  31 ++
 src/plugins/ctags/ide-ctags-builder.c              |  34 ++-
 src/plugins/ctags/ide-ctags-builder.h              |   6 +-
 src/plugins/ctags/ide-ctags-completion-item.h      |   3 +-
 .../ctags/ide-ctags-completion-provider-private.h  |   2 +
 src/plugins/ctags/ide-ctags-completion-provider.c  |  21 +-
 src/plugins/ctags/ide-ctags-completion-provider.h  |   2 +-
 src/plugins/ctags/ide-ctags-highlighter.c          |  33 +--
 src/plugins/ctags/ide-ctags-highlighter.h          |   2 +-
 src/plugins/ctags/ide-ctags-index.c                |   3 +-
 src/plugins/ctags/ide-ctags-index.h                |  24 +-
 src/plugins/ctags/ide-ctags-preferences-addin.c    |   2 +-
 src/plugins/ctags/ide-ctags-results.h              |   2 +-
 src/plugins/ctags/ide-ctags-service.c              | 311 +++++++++++++++++----
 src/plugins/ctags/ide-ctags-service.h              |  24 +-
 src/plugins/ctags/ide-ctags-symbol-node.c          |   6 +-
 src/plugins/ctags/ide-ctags-symbol-node.h          |   2 +-
 src/plugins/ctags/ide-ctags-symbol-resolver.c      |  73 +++--
 src/plugins/ctags/ide-ctags-symbol-resolver.h      |   5 +-
 src/plugins/ctags/ide-ctags-symbol-tree.c          |   4 +-
 src/plugins/ctags/ide-ctags-symbol-tree.h          |   2 +-
 src/plugins/ctags/ide-ctags-util.c                 |  14 +-
 src/plugins/ctags/ide-tags-builder.c               |  58 ++++
 src/plugins/ctags/ide-tags-builder.h               |  56 ++++
 src/plugins/ctags/meson.build                      |  34 ++-
 src/plugins/ctags/test-ctags.c                     | 110 ++++++++
 src/plugins/ctags/test-tags                        |  28 ++
 31 files changed, 918 insertions(+), 208 deletions(-)
---
diff --git a/src/plugins/ctags/ctags-plugin.c b/src/plugins/ctags/ctags-plugin.c
index 90348f91f..af6b794fb 100644
--- a/src/plugins/ctags/ctags-plugin.c
+++ b/src/plugins/ctags/ctags-plugin.c
@@ -18,26 +18,42 @@
  * SPDX-License-Identifier: GPL-3.0-or-later
  */
 
+#include "config.h"
+
 #include <libpeas/peas.h>
 #include <gtksourceview/gtksource.h>
+#include <libide-code.h>
+#include <libide-gui.h>
+#include <libide-sourceview.h>
+#include <libide-io.h>
 
+#include "gbp-ctags-workbench-addin.h"
 #include "ide-ctags-builder.h"
 #include "ide-ctags-completion-item.h"
 #include "ide-ctags-completion-provider.h"
 #include "ide-ctags-highlighter.h"
 #include "ide-ctags-index.h"
 #include "ide-ctags-preferences-addin.h"
-#include "ide-ctags-service.h"
 #include "ide-ctags-symbol-resolver.h"
 
-void
-ide_ctags_register_types (PeasObjectModule *module)
+_IDE_EXTERN void
+_ide_ctags_register_types (PeasObjectModule *module)
 {
-  peas_object_module_register_extension_type (module, IDE_TYPE_COMPLETION_PROVIDER, 
IDE_TYPE_CTAGS_COMPLETION_PROVIDER);
-  peas_object_module_register_extension_type (module, IDE_TYPE_HIGHLIGHTER, IDE_TYPE_CTAGS_HIGHLIGHTER);
-  peas_object_module_register_extension_type (module, IDE_TYPE_SERVICE, IDE_TYPE_CTAGS_SERVICE);
-  peas_object_module_register_extension_type (module, IDE_TYPE_PREFERENCES_ADDIN, 
IDE_TYPE_CTAGS_PREFERENCES_ADDIN);
-  peas_object_module_register_extension_type (module, IDE_TYPE_SYMBOL_RESOLVER, 
IDE_TYPE_CTAGS_SYMBOL_RESOLVER);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_COMPLETION_PROVIDER,
+                                              IDE_TYPE_CTAGS_COMPLETION_PROVIDER);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_HIGHLIGHTER,
+                                              IDE_TYPE_CTAGS_HIGHLIGHTER);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_PREFERENCES_ADDIN,
+                                              IDE_TYPE_CTAGS_PREFERENCES_ADDIN);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_SYMBOL_RESOLVER,
+                                              IDE_TYPE_CTAGS_SYMBOL_RESOLVER);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_WORKBENCH_ADDIN,
+                                              GBP_TYPE_CTAGS_WORKBENCH_ADDIN);
 
-  ide_vcs_register_ignored ("tags.??????");
+  ide_g_file_add_ignored_pattern ("tags.??????");
 }
diff --git a/src/plugins/ctags/ctags.gresource.xml b/src/plugins/ctags/ctags.gresource.xml
index 6f6c91d16..fa181ff16 100644
--- a/src/plugins/ctags/ctags.gresource.xml
+++ b/src/plugins/ctags/ctags.gresource.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <gresources>
-  <gresource prefix="/org/gnome/builder/plugins">
+  <gresource prefix="/plugins/ctags">
     <file>ctags.plugin</file>
   </gresource>
 </gresources>
diff --git a/src/plugins/ctags/ctags.plugin b/src/plugins/ctags/ctags.plugin
index 48543afba..1a29b388f 100644
--- a/src/plugins/ctags/ctags.plugin
+++ b/src/plugins/ctags/ctags.plugin
@@ -1,11 +1,12 @@
 [Plugin]
-Module=ctags-plugin
-Name=Ctags Auto-Completion
-Description=Provides integration with Ctags for auto-completion and symbol resolving
 Authors=Christian Hergert <christian hergert me>
-Copyright=Copyright © 2015 Christian Hergert
 Builtin=true
-Embedded=ide_ctags_register_types
+Copyright=Copyright © 2015-2018 Christian Hergert
+Depends=editor;
+Description=Provides integration with Ctags for auto-completion and symbol resolving
+Embedded=_ide_ctags_register_types
+Module=ctags
+Name=Ctags Auto-Completion
 X-Completion-Provider-Languages=c,cpp,chdr,cpphdr,python,python3,js,ruby
-X-Highlighter-Languages=c,cpp,chdr,python,python3,js,ruby
-X-Symbol-Resolver-Languages=c,cpp,chdr,python,python3,js,css,html,ruby
+X-Highlighter-Languages=c,cpp,chdr,cpphdr,python,python3,js,ruby
+X-Symbol-Resolver-Languages=c,cpp,chdr,cpphdr,python,python3,js,css,html,ruby
diff --git a/src/plugins/ctags/gbp-ctags-workbench-addin.c b/src/plugins/ctags/gbp-ctags-workbench-addin.c
new file mode 100644
index 000000000..7ab17d797
--- /dev/null
+++ b/src/plugins/ctags/gbp-ctags-workbench-addin.c
@@ -0,0 +1,183 @@
+/* gbp-ctags-workbench-addin.c
+ *
+ * Copyright 2018-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-ctags-workbench-addin"
+
+#include "config.h"
+
+#include <libide-gui.h>
+
+#include "gbp-ctags-workbench-addin.h"
+#include "ide-ctags-service.h"
+
+struct _GbpCtagsWorkbenchAddin
+{
+  GObject       parent_instance;
+  IdeWorkbench *workbench;
+};
+
+static void
+gbp_ctags_workbench_addin_load_project_async (IdeWorkbenchAddin   *addin,
+                                              IdeProjectInfo      *project_info,
+                                              GCancellable        *cancellable,
+                                              GAsyncReadyCallback  callback,
+                                              gpointer             user_data)
+{
+  GbpCtagsWorkbenchAddin *self = (GbpCtagsWorkbenchAddin *)addin;
+  g_autoptr(IdeTask) task = NULL;
+  g_autoptr(IdeCtagsService) service = NULL;
+  IdeContext *context;
+
+  g_assert (GBP_IS_CTAGS_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_PROJECT_INFO (project_info));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (addin, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_ctags_workbench_addin_load_project_async);
+
+  /* We don't load the ctags service until a project is loaded so that
+   * we have a stable workdir to use.
+   */
+  context = ide_workbench_get_context (self->workbench);
+  service = ide_object_ensure_child_typed (IDE_OBJECT (context), IDE_TYPE_CTAGS_SERVICE);
+
+  ide_task_return_boolean (task, TRUE);
+}
+
+static gboolean
+gbp_ctags_workbench_addin_load_project_finish (IdeWorkbenchAddin  *addin,
+                                               GAsyncResult       *result,
+                                               GError            **error)
+{
+  g_assert (GBP_IS_CTAGS_WORKBENCH_ADDIN (addin));
+  g_assert (IDE_IS_TASK (result));
+
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+gbp_ctags_workbench_addin_load (IdeWorkbenchAddin *addin,
+                                IdeWorkbench      *workbench)
+{
+  GbpCtagsWorkbenchAddin *self = (GbpCtagsWorkbenchAddin *)addin;
+
+  g_assert (GBP_IS_CTAGS_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  self->workbench = workbench;
+}
+
+static void
+gbp_ctags_workbench_addin_unload (IdeWorkbenchAddin *addin,
+                                  IdeWorkbench      *workbench)
+{
+  GbpCtagsWorkbenchAddin *self = (GbpCtagsWorkbenchAddin *)addin;
+
+  g_assert (GBP_IS_CTAGS_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKBENCH (workbench));
+
+  self->workbench = NULL;
+}
+
+static void
+pause_ctags_cb (GSimpleAction *action,
+                GVariant      *param,
+                gpointer       user_data)
+{
+  GbpCtagsWorkbenchAddin *self = user_data;
+  IdeContext *context;
+  IdeCtagsService *service;
+
+  g_assert (G_IS_SIMPLE_ACTION (action));
+  g_assert (GBP_IS_CTAGS_WORKBENCH_ADDIN (self));
+  g_assert (g_variant_is_of_type (param, G_VARIANT_TYPE_BOOLEAN));
+
+  if ((context = ide_workbench_get_context (self->workbench)) &&
+      (service = ide_context_peek_child_typed (context, IDE_TYPE_CTAGS_SERVICE)))
+    {
+      gboolean val;
+
+      if (g_variant_get_boolean (param))
+        ide_ctags_service_pause (service);
+      else
+        ide_ctags_service_unpause (service);
+
+      /* Re-fetch the value incase we were out-of-sync */
+      g_object_get (service, "paused", &val, NULL);
+      g_simple_action_set_state (action, g_variant_new_boolean (val));
+    }
+}
+
+static const GActionEntry actions[] = {
+  { "pause-ctags", NULL, NULL, "false", pause_ctags_cb },
+};
+
+static void
+gbp_ctags_workbench_addin_workspace_added (IdeWorkbenchAddin *addin,
+                                           IdeWorkspace      *workspace)
+{
+  GbpCtagsWorkbenchAddin *self = (GbpCtagsWorkbenchAddin *)addin;
+
+  g_assert (GBP_IS_CTAGS_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKSPACE (workspace));
+
+  g_action_map_add_action_entries (G_ACTION_MAP (workspace),
+                                   actions,
+                                   G_N_ELEMENTS (actions),
+                                   self);
+}
+
+static void
+gbp_ctags_workbench_addin_workspace_removed (IdeWorkbenchAddin *addin,
+                                             IdeWorkspace      *workspace)
+{
+  GbpCtagsWorkbenchAddin *self = (GbpCtagsWorkbenchAddin *)addin;
+
+  g_assert (GBP_IS_CTAGS_WORKBENCH_ADDIN (self));
+  g_assert (IDE_IS_WORKSPACE (workspace));
+
+  for (guint i = 0; i < G_N_ELEMENTS (actions); i++)
+    g_action_map_remove_action (G_ACTION_MAP (workspace), actions[i].name);
+}
+
+static void
+workbench_addin_iface_init (IdeWorkbenchAddinInterface *iface)
+{
+  iface->load = gbp_ctags_workbench_addin_load;
+  iface->unload = gbp_ctags_workbench_addin_unload;
+  iface->load_project_async = gbp_ctags_workbench_addin_load_project_async;
+  iface->load_project_finish = gbp_ctags_workbench_addin_load_project_finish;
+  iface->workspace_added = gbp_ctags_workbench_addin_workspace_added;
+  iface->workspace_removed = gbp_ctags_workbench_addin_workspace_removed;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpCtagsWorkbenchAddin, gbp_ctags_workbench_addin, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_WORKBENCH_ADDIN,
+                                                workbench_addin_iface_init))
+
+static void
+gbp_ctags_workbench_addin_class_init (GbpCtagsWorkbenchAddinClass *klass)
+{
+}
+
+static void
+gbp_ctags_workbench_addin_init (GbpCtagsWorkbenchAddin *self)
+{
+}
diff --git a/src/plugins/ctags/gbp-ctags-workbench-addin.h b/src/plugins/ctags/gbp-ctags-workbench-addin.h
new file mode 100644
index 000000000..f8918c6f9
--- /dev/null
+++ b/src/plugins/ctags/gbp-ctags-workbench-addin.h
@@ -0,0 +1,31 @@
+/* gbp-ctags-workbench-addin.h
+ *
+ * Copyright 2018-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 <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_CTAGS_WORKBENCH_ADDIN (gbp_ctags_workbench_addin_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpCtagsWorkbenchAddin, gbp_ctags_workbench_addin, GBP, CTAGS_WORKBENCH_ADDIN, GObject)
+
+G_END_DECLS
diff --git a/src/plugins/ctags/ide-ctags-builder.c b/src/plugins/ctags/ide-ctags-builder.c
index 3b535d8be..63ae07f58 100644
--- a/src/plugins/ctags/ide-ctags-builder.c
+++ b/src/plugins/ctags/ide-ctags-builder.c
@@ -20,6 +20,8 @@
 
 #define G_LOG_DOMAIN "ide-ctags-builder"
 
+#include <libide-vcs.h>
+
 #include "ide-ctags-builder.h"
 
 struct _IdeCtagsBuilder
@@ -79,13 +81,9 @@ ide_ctags_builder_init (IdeCtagsBuilder *self)
 }
 
 IdeTagsBuilder *
-ide_ctags_builder_new (IdeContext *context)
+ide_ctags_builder_new (void)
 {
-  g_return_val_if_fail (IDE_IS_CONTEXT (context), NULL);
-
-  return g_object_new (IDE_TYPE_CTAGS_BUILDER,
-                       "context", context,
-                       NULL);
+  return g_object_new (IDE_TYPE_CTAGS_BUILDER, NULL);
 }
 
 static gboolean
@@ -108,9 +106,9 @@ ide_ctags_builder_build (IdeCtagsBuilder *self,
   g_autofree gchar *options_path = NULL;
   g_autofree gchar *tags_path = NULL;
   g_autoptr(GString) filenames = NULL;
+  g_autoptr(IdeVcs) vcs = NULL;
   GOutputStream *stdin_stream;
   IdeContext *context;
-  IdeVcs *vcs;
   gpointer infoptr;
 
   g_assert (IDE_IS_CTAGS_BUILDER (self));
@@ -118,7 +116,7 @@ ide_ctags_builder_build (IdeCtagsBuilder *self,
   g_assert (G_IS_FILE (destination));
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  vcs = ide_context_get_vcs (context);
+  vcs = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_VCS);
 
   dest_dir = g_file_get_path (destination);
   if (0 != g_mkdir_with_parents (dest_dir, 0750))
@@ -305,15 +303,28 @@ ide_ctags_builder_build_async (IdeTagsBuilder      *builder,
   g_autoptr(GSettings) settings = NULL;
   g_autofree gchar *destination_path = NULL;
   g_autofree gchar *relative_path = NULL;
+  g_autoptr(GFile) workdir = NULL;
   BuildTaskData *task_data;
   IdeContext *context;
-  GFile *workdir;
 
   IDE_ENTRY;
 
   g_assert (IDE_IS_CTAGS_BUILDER (self));
   g_assert (G_IS_FILE (directory_or_file));
 
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, ide_ctags_builder_build_async);
+  ide_task_set_priority (task, G_PRIORITY_LOW + 200);
+
+  if (ide_object_in_destruction (IDE_OBJECT (self)))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_CANCELLED,
+                                 "The operation was cancelled");
+      IDE_EXIT;
+    }
+
   settings = g_settings_new ("org.gnome.builder.code-insight");
 
   task_data = g_slice_new0 (BuildTaskData);
@@ -329,14 +340,11 @@ ide_ctags_builder_build_async (IdeTagsBuilder      *builder,
    * putting things in the source tree.
    */
   context = ide_object_get_context (IDE_OBJECT (self));
-  workdir = ide_vcs_get_working_directory (ide_context_get_vcs (context));
+  workdir = ide_context_ref_workdir (context);
   relative_path = g_file_get_relative_path (workdir, directory_or_file);
   destination_path = ide_context_cache_filename (context, "ctags", relative_path, NULL);
   task_data->destination = g_file_new_for_path (destination_path);
 
-  task = ide_task_new (self, cancellable, callback, user_data);
-  ide_task_set_source_tag (task, ide_ctags_builder_build_async);
-  ide_task_set_priority (task, G_PRIORITY_LOW + 200);
   ide_task_set_task_data (task, task_data, build_task_data_free);
   ide_task_set_kind (task, IDE_TASK_KIND_INDEXER);
   ide_task_run_in_thread (task, ide_ctags_builder_build_worker);
diff --git a/src/plugins/ctags/ide-ctags-builder.h b/src/plugins/ctags/ide-ctags-builder.h
index b5d75a9e3..40390847d 100644
--- a/src/plugins/ctags/ide-ctags-builder.h
+++ b/src/plugins/ctags/ide-ctags-builder.h
@@ -20,7 +20,9 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-code.h>
+
+#include "ide-tags-builder.h"
 
 G_BEGIN_DECLS
 
@@ -28,6 +30,6 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeCtagsBuilder, ide_ctags_builder, IDE, CTAGS_BUILDER, IdeObject)
 
-IdeTagsBuilder *ide_ctags_builder_new (IdeContext *context);
+IdeTagsBuilder *ide_ctags_builder_new (void);
 
 G_END_DECLS
diff --git a/src/plugins/ctags/ide-ctags-completion-item.h b/src/plugins/ctags/ide-ctags-completion-item.h
index c38977328..1894be5fc 100644
--- a/src/plugins/ctags/ide-ctags-completion-item.h
+++ b/src/plugins/ctags/ide-ctags-completion-item.h
@@ -20,7 +20,8 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-code.h>
+#include <libide-sourceview.h>
 
 #include "ide-ctags-index.h"
 #include "ide-ctags-results.h"
diff --git a/src/plugins/ctags/ide-ctags-completion-provider-private.h 
b/src/plugins/ctags/ide-ctags-completion-provider-private.h
index 00212f5b4..a62e746b7 100644
--- a/src/plugins/ctags/ide-ctags-completion-provider-private.h
+++ b/src/plugins/ctags/ide-ctags-completion-provider-private.h
@@ -20,6 +20,8 @@
 
 #pragma once
 
+#include <libide-core.h>
+
 #include "ide-ctags-completion-provider.h"
 
 G_BEGIN_DECLS
diff --git a/src/plugins/ctags/ide-ctags-completion-provider.c 
b/src/plugins/ctags/ide-ctags-completion-provider.c
index 13d7d7d0c..afc9a427a 100644
--- a/src/plugins/ctags/ide-ctags-completion-provider.c
+++ b/src/plugins/ctags/ide-ctags-completion-provider.c
@@ -21,8 +21,7 @@
 #define G_LOG_DOMAIN "ide-ctags-completion-provider"
 
 #include <glib/gi18n.h>
-
-#include "sourceview/ide-source-iter.h"
+#include <libide-code.h>
 
 #include "ide-ctags-completion-item.h"
 #include "ide-ctags-completion-provider.h"
@@ -141,24 +140,24 @@ ide_ctags_completion_provider_load (IdeCompletionProvider *provider,
                                     IdeContext            *context)
 {
   IdeCtagsCompletionProvider *self = (IdeCtagsCompletionProvider *)provider;
-  IdeCtagsService *service;
+  g_autoptr(IdeCtagsService) service = NULL;
 
   g_assert (IDE_IS_CTAGS_COMPLETION_PROVIDER (self));
   g_assert (IDE_IS_CONTEXT (context));
 
-  service = ide_context_get_service_typed (context, IDE_TYPE_CTAGS_SERVICE);
-  ide_ctags_service_register_completion (service, self);
+  if ((service = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_CTAGS_SERVICE)))
+    ide_ctags_service_register_completion (service, self);
 }
 
 static void
 ide_ctags_completion_provider_dispose (GObject *object)
 {
   IdeCtagsCompletionProvider *self = (IdeCtagsCompletionProvider *)object;
+  g_autoptr(IdeCtagsService) service = NULL;
   IdeContext *context;
-  IdeCtagsService *service;
 
   if ((context = ide_object_get_context (IDE_OBJECT (self))) &&
-      (service = ide_context_get_service_typed (context, IDE_TYPE_CTAGS_SERVICE)))
+      (service = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_CTAGS_SERVICE)))
     ide_ctags_service_unregister_completion (service, self);
 
   G_OBJECT_CLASS (ide_ctags_completion_provider_parent_class)->dispose (object);
@@ -330,12 +329,11 @@ ide_ctags_completion_provider_activate_proposal (IdeCompletionProvider *provider
   IdeCtagsCompletionItem *item = (IdeCtagsCompletionItem *)proposal;
   g_autofree gchar *slice = NULL;
   g_autoptr(IdeSnippet) snippet = NULL;
-  IdeFileSettings *file_settings = NULL;
+  IdeFileSettings *file_settings;
   GtkTextBuffer *buffer;
   GtkTextView *view;
   GtkTextIter begin;
   GtkTextIter end;
-  IdeFile *file;
 
   g_assert (IDE_IS_CTAGS_COMPLETION_PROVIDER (provider));
   g_assert (IDE_IS_CTAGS_COMPLETION_ITEM (item));
@@ -349,10 +347,7 @@ ide_ctags_completion_provider_activate_proposal (IdeCompletionProvider *provider
   buffer = ide_completion_context_get_buffer (context);
   g_assert (IDE_IS_BUFFER (buffer));
 
-  file = ide_buffer_get_file (IDE_BUFFER (buffer));
-  g_assert (IDE_IS_FILE (file));
-
-  file_settings = ide_file_peek_settings (file);
+  file_settings = ide_buffer_get_file_settings (IDE_BUFFER (buffer));
   g_assert (!file_settings || IDE_IS_FILE_SETTINGS (file_settings));
 
   slice = gtk_text_iter_get_slice (&begin, &end);
diff --git a/src/plugins/ctags/ide-ctags-completion-provider.h 
b/src/plugins/ctags/ide-ctags-completion-provider.h
index 0e9220b23..47fd9d77e 100644
--- a/src/plugins/ctags/ide-ctags-completion-provider.h
+++ b/src/plugins/ctags/ide-ctags-completion-provider.h
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-core.h>
 
 #include "ide-ctags-index.h"
 
diff --git a/src/plugins/ctags/ide-ctags-highlighter.c b/src/plugins/ctags/ide-ctags-highlighter.c
index 61cbe7e94..1f63c97e4 100644
--- a/src/plugins/ctags/ide-ctags-highlighter.c
+++ b/src/plugins/ctags/ide-ctags-highlighter.c
@@ -107,25 +107,26 @@ get_tag_from_kind (IdeCtagsIndexEntryKind kind)
 
 static const gchar *
 get_tag (IdeCtagsHighlighter *self,
-         IdeFile             *file,
+         GFile               *file,
          const gchar         *word)
 {
-  const gchar *file_path = ide_file_get_path (file);
+  const gchar *file_path = g_file_peek_path (file);
   const IdeCtagsIndexEntry *entries;
   gsize n_entries;
-  gsize i;
-  gsize j;
 
-  for (i = 0; i < self->indexes->len; i++)
+  for (guint i = 0; i < self->indexes->len; i++)
     {
       IdeCtagsIndex *item = g_ptr_array_index (self->indexes, i);
+
       entries = ide_ctags_index_lookup_prefix (item, word, &n_entries);
       if ((entries == NULL) || (n_entries == 0))
         continue;
 
-      for (j = 0; j < n_entries; j++)
-        if (dzl_str_equal0 (entries[j].path, file_path))
-          return get_tag_from_kind (entries[j].kind);
+      for (guint j = 0; j < n_entries; j++)
+        {
+          if (ide_str_equal0 (entries[j].path, file_path))
+            return get_tag_from_kind (entries[j].kind);
+        }
 
       return get_tag_from_kind (entries[0].kind);
     }
@@ -143,7 +144,7 @@ ide_ctags_highlighter_real_update (IdeHighlighter       *highlighter,
   GtkTextBuffer *text_buffer;
   GtkSourceBuffer *source_buffer;
   IdeBuffer *buffer;
-  IdeFile *file;
+  GFile *file;
   GtkTextIter begin;
   GtkTextIter end;
 
@@ -243,20 +244,20 @@ ide_ctags_highlighter_real_set_engine (IdeHighlighter      *highlighter,
                                        IdeHighlightEngine  *engine)
 {
   IdeCtagsHighlighter *self = (IdeCtagsHighlighter *)highlighter;
+  g_autoptr(IdeCtagsService) service = NULL;
   IdeContext *context;
-  IdeCtagsService *service;
 
   g_return_if_fail (IDE_IS_CTAGS_HIGHLIGHTER (self));
   g_return_if_fail (IDE_IS_HIGHLIGHT_ENGINE (engine));
 
   self->engine = engine;
 
-  context = ide_object_get_context (IDE_OBJECT (self));
-  service = ide_context_get_service_typed (context, IDE_TYPE_CTAGS_SERVICE);
-
-  g_set_weak_pointer (&self->service, service);
-
-  ide_ctags_service_register_highlighter (service, self);
+  if ((context = ide_object_get_context (IDE_OBJECT (self))) &&
+      (service = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_CTAGS_SERVICE)))
+    {
+      g_set_weak_pointer (&self->service, service);
+      ide_ctags_service_register_highlighter (service, self);
+    }
 }
 
 static void
diff --git a/src/plugins/ctags/ide-ctags-highlighter.h b/src/plugins/ctags/ide-ctags-highlighter.h
index 5c2bfa56b..600051cd8 100644
--- a/src/plugins/ctags/ide-ctags-highlighter.h
+++ b/src/plugins/ctags/ide-ctags-highlighter.h
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-core.h>
 
 #include "ide-ctags-index.h"
 
diff --git a/src/plugins/ctags/ide-ctags-index.c b/src/plugins/ctags/ide-ctags-index.c
index 60d6595f3..da1bc7826 100644
--- a/src/plugins/ctags/ide-ctags-index.c
+++ b/src/plugins/ctags/ide-ctags-index.c
@@ -22,7 +22,6 @@
 
 #include <dazzle.h>
 #include <glib/gi18n.h>
-#include <ide.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -295,7 +294,7 @@ ide_ctags_index_set_path_root (IdeCtagsIndex *self,
 {
   g_return_if_fail (IDE_IS_CTAGS_INDEX (self));
 
-  if (!dzl_str_equal0 (self->path_root, path_root))
+  if (!ide_str_equal0 (self->path_root, path_root))
     {
       g_free (self->path_root);
       self->path_root = g_strdup (path_root);
diff --git a/src/plugins/ctags/ide-ctags-index.h b/src/plugins/ctags/ide-ctags-index.h
index 8704b1065..aabc79692 100644
--- a/src/plugins/ctags/ide-ctags-index.h
+++ b/src/plugins/ctags/ide-ctags-index.h
@@ -20,8 +20,8 @@
 
 #pragma once
 
-#include <gio/gio.h>
-#include <ide.h>
+#include <libide-core.h>
+#include <libide-code.h>
 
 G_BEGIN_DECLS
 
@@ -101,37 +101,37 @@ ide_ctags_index_entry_kind_to_symbol_kind (IdeCtagsIndexEntryKind kind)
     case IDE_CTAGS_INDEX_ENTRY_PROTOTYPE:
       /* bit of an impedenece mismatch */
     case IDE_CTAGS_INDEX_ENTRY_CLASS_NAME:
-      return IDE_SYMBOL_CLASS;
+      return IDE_SYMBOL_KIND_CLASS;
 
     case IDE_CTAGS_INDEX_ENTRY_ENUMERATOR:
-      return IDE_SYMBOL_ENUM;
+      return IDE_SYMBOL_KIND_ENUM;
 
     case IDE_CTAGS_INDEX_ENTRY_ENUMERATION_NAME:
-      return IDE_SYMBOL_ENUM_VALUE;
+      return IDE_SYMBOL_KIND_ENUM_VALUE;
 
     case IDE_CTAGS_INDEX_ENTRY_FUNCTION:
-      return IDE_SYMBOL_FUNCTION;
+      return IDE_SYMBOL_KIND_FUNCTION;
 
     case IDE_CTAGS_INDEX_ENTRY_MEMBER:
-      return IDE_SYMBOL_FIELD;
+      return IDE_SYMBOL_KIND_FIELD;
 
     case IDE_CTAGS_INDEX_ENTRY_STRUCTURE:
-      return IDE_SYMBOL_STRUCT;
+      return IDE_SYMBOL_KIND_STRUCT;
 
     case IDE_CTAGS_INDEX_ENTRY_UNION:
-      return IDE_SYMBOL_UNION;
+      return IDE_SYMBOL_KIND_UNION;
 
     case IDE_CTAGS_INDEX_ENTRY_VARIABLE:
-      return IDE_SYMBOL_VARIABLE;
+      return IDE_SYMBOL_KIND_VARIABLE;
 
     case IDE_CTAGS_INDEX_ENTRY_IMPORT:
-      return IDE_SYMBOL_PACKAGE;
+      return IDE_SYMBOL_KIND_PACKAGE;
 
     case IDE_CTAGS_INDEX_ENTRY_ANCHOR:
     case IDE_CTAGS_INDEX_ENTRY_DEFINE:
     case IDE_CTAGS_INDEX_ENTRY_FILE_NAME:
     default:
-      return IDE_SYMBOL_NONE;
+      return IDE_SYMBOL_KIND_NONE;
     }
 }
 
diff --git a/src/plugins/ctags/ide-ctags-preferences-addin.c b/src/plugins/ctags/ide-ctags-preferences-addin.c
index 34839f1b2..586aa55e8 100644
--- a/src/plugins/ctags/ide-ctags-preferences-addin.c
+++ b/src/plugins/ctags/ide-ctags-preferences-addin.c
@@ -23,7 +23,7 @@
 #include "config.h"
 
 #include <glib/gi18n.h>
-#include <ide.h>
+#include <libide-gui.h>
 
 #include "ide-ctags-preferences-addin.h"
 
diff --git a/src/plugins/ctags/ide-ctags-results.h b/src/plugins/ctags/ide-ctags-results.h
index 67eac2134..5336974d5 100644
--- a/src/plugins/ctags/ide-ctags-results.h
+++ b/src/plugins/ctags/ide-ctags-results.h
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-sourceview.h>
 
 #include "ide-ctags-index.h"
 
diff --git a/src/plugins/ctags/ide-ctags-service.c b/src/plugins/ctags/ide-ctags-service.c
index a4fedbac9..dd080775e 100644
--- a/src/plugins/ctags/ide-ctags-service.c
+++ b/src/plugins/ctags/ide-ctags-service.c
@@ -23,12 +23,15 @@
 #include <dazzle.h>
 #include <glib/gi18n.h>
 #include <gtksourceview/gtksource.h>
+#include <libide-code.h>
+#include <libide-vcs.h>
 
 #include "ide-ctags-builder.h"
 #include "ide-ctags-completion-provider.h"
 #include "ide-ctags-highlighter.h"
 #include "ide-ctags-index.h"
 #include "ide-ctags-service.h"
+#include "ide-tags-builder.h"
 
 #define QUEUED_BUILD_TIMEOUT_SECS 5
 
@@ -42,8 +45,12 @@ struct _IdeCtagsService
   GPtrArray        *completions;
   GHashTable       *build_timeout_by_dir;
 
+  IdeNotification  *notif;
+  gint              n_active;
+
   guint             queued_miner_handler;
   guint             miner_active : 1;
+  guint             paused : 1;
 };
 
 typedef struct
@@ -59,10 +66,15 @@ typedef struct
   gboolean         recursive;
 } QueuedRequest;
 
-static void service_iface_init (IdeServiceInterface *iface);
+G_DEFINE_TYPE (IdeCtagsService, ide_ctags_service, IDE_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_PAUSED,
+  N_PROPS
+};
 
-G_DEFINE_TYPE_WITH_CODE (IdeCtagsService, ide_ctags_service, IDE_TYPE_OBJECT,
-                         G_IMPLEMENT_INTERFACE (IDE_TYPE_SERVICE, service_iface_init))
+static GParamSpec *properties [N_PROPS];
 
 static void
 queued_request_free (gpointer data)
@@ -74,6 +86,75 @@ queued_request_free (gpointer data)
   g_slice_free (QueuedRequest, qr);
 }
 
+static gboolean
+is_supported_language (const gchar *lang_id)
+{
+  /* Some languages we expect ctags to actually parse when saved.
+   * Keep in sync with our .plugin file.
+   */
+  static const gchar *supported[] = {
+    "c", "cpp", "chdr", "cpphdr", "python", "python3",
+    "js", "css", "html", "ruby",
+    NULL
+  };
+
+  if (lang_id == NULL)
+    return FALSE;
+
+  return g_strv_contains (supported, lang_id);
+}
+
+static void
+show_notification (IdeCtagsService *self)
+{
+  g_autoptr(IdeObject) root = NULL;
+  g_autoptr(IdeNotification) notif = NULL;
+  g_autoptr(IdeNotifications) notifs = NULL;
+  g_autoptr(GIcon) icon = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CTAGS_SERVICE (self));
+  g_assert (self->n_active >= 0);
+
+  self->n_active++;
+
+  if (self->n_active > 1)
+    return;
+
+  g_assert (self->notif == NULL);
+
+  root = ide_object_ref_root (IDE_OBJECT (self));
+  notifs = ide_object_get_child_typed (root, IDE_TYPE_NOTIFICATIONS);
+
+  notif = ide_notification_new ();
+  icon = g_icon_new_for_string ("media-playback-pause-symbolic", NULL);
+  ide_notification_set_title (notif, _("Indexing Source Code"));
+  ide_notification_set_body (notif, _("Search, autocompletion, and symbol information may be limited until 
Ctags indexing is complete."));
+  ide_notification_set_has_progress (notif, TRUE);
+  ide_notification_set_progress_is_imprecise (notif, TRUE);
+  ide_notification_add_button (notif, NULL, icon, "win.pause-ctags");
+  ide_notifications_add_notification (notifs, notif);
+
+  self->notif = g_steal_pointer (&notif);
+}
+
+static void
+hide_notification (IdeCtagsService *self)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_CTAGS_SERVICE (self));
+  g_assert (self->notif != NULL);
+  g_assert (self->n_active > 0);
+
+  self->n_active--;
+
+  if (self->n_active == 0)
+    {
+      ide_notification_withdraw_in_seconds (self->notif, 3);
+      g_clear_object (&self->notif);
+    }
+}
+
 static void
 ide_ctags_service_build_index_init_cb (GObject      *object,
                                        GAsyncResult *result,
@@ -119,13 +200,11 @@ resolve_path_root (IdeCtagsService *self,
 {
   g_autoptr(GFile) parent = NULL;
   g_autoptr(GFile) cache_file = NULL;
+  g_autoptr(GFile) workdir = NULL;
   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);
+  workdir = ide_context_ref_workdir (context);
   parent = g_file_get_parent (file);
 
   /*
@@ -383,8 +462,8 @@ ide_ctags_service_miner (GTask        *task,
                          GCancellable *cancellable)
 {
   IdeCtagsService *self = source_object;
+  g_autoptr(IdeVcs) vcs = NULL;
   IdeContext *context;
-  IdeVcs *vcs;
   GArray *mine_info = task_data;
 
   IDE_ENTRY;
@@ -394,7 +473,7 @@ ide_ctags_service_miner (GTask        *task,
   g_assert (mine_info != NULL);
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  vcs = ide_context_get_vcs (context);
+  vcs = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_VCS);
 
   for (guint i = 0; i < mine_info->len; i++)
     {
@@ -423,9 +502,9 @@ ide_ctags_service_do_mine (gpointer data)
   IdeCtagsService *self = data;
   g_autoptr(GTask) task = NULL;
   g_autoptr(GArray) mine_info = NULL;
+  g_autoptr(GFile) workdir = NULL;
   IdeContext *context;
   MineInfo info;
-  GFile *workdir;
 
   IDE_ENTRY;
 
@@ -435,7 +514,7 @@ ide_ctags_service_do_mine (gpointer data)
   self->miner_active = TRUE;
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  workdir = ide_vcs_get_working_directory (ide_context_get_vcs (context));
+  workdir = ide_context_ref_workdir (context);
 
   mine_info = g_array_new (FALSE, FALSE, sizeof (MineInfo));
   g_array_set_clear_func (mine_info, clear_mine_info);
@@ -489,20 +568,21 @@ build_system_tags_cb (GObject      *object,
 {
   g_autoptr(IdeCtagsService) self = user_data;
 
+  g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_TAGS_BUILDER (object));
   g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (IDE_IS_CTAGS_SERVICE (self));
 
+  ide_object_destroy (IDE_OBJECT (object));
   ide_ctags_service_queue_mine (self);
+  hide_notification (self);
 }
 
 static gboolean
 restart_miner (gpointer user_data)
 {
-  QueuedRequest *qr = user_data;
   g_autoptr(IdeTagsBuilder) tags_builder = NULL;
-  IdeBuildSystem *build_system;
-  IdeContext *context;
+  QueuedRequest *qr = user_data;
 
   IDE_ENTRY;
 
@@ -510,20 +590,21 @@ restart_miner (gpointer user_data)
   g_assert (IDE_IS_CTAGS_SERVICE (qr->self));
   g_assert (G_IS_FILE (qr->directory));
 
+  /* Just skip for now if we got here somehow and paused */
+  if (qr->self->paused)
+    IDE_RETURN (G_SOURCE_CONTINUE);
+
   g_hash_table_remove (qr->self->build_timeout_by_dir, qr->directory);
 
-  context = ide_object_get_context (IDE_OBJECT (qr->self));
-  build_system = ide_context_get_build_system (context);
+  tags_builder = ide_ctags_builder_new ();
+  ide_object_append (IDE_OBJECT (qr->self), IDE_OBJECT (tags_builder));
 
-  if (IDE_IS_TAGS_BUILDER (build_system))
-    tags_builder = g_object_ref (IDE_TAGS_BUILDER (build_system));
-  else
-    tags_builder = ide_ctags_builder_new (context);
+  show_notification (qr->self);
 
   ide_tags_builder_build_async (tags_builder,
                                 qr->directory,
                                 qr->recursive,
-                                NULL,
+                                qr->self->cancellable,
                                 build_system_tags_cb,
                                 g_object_ref (qr->self));
 
@@ -538,13 +619,14 @@ ide_ctags_service_queue_build_for_directory (IdeCtagsService *self,
   g_assert (IDE_IS_CTAGS_SERVICE (self));
   g_assert (G_IS_FILE (directory));
 
-  if (ide_object_is_unloading (IDE_OBJECT (self)))
+  if (ide_object_in_destruction (IDE_OBJECT (self)))
     return;
 
   if (!g_hash_table_lookup (self->build_timeout_by_dir, directory))
     {
       QueuedRequest *qr;
-      guint source_id;
+      GSource *source;
+      guint source_id = 0;
 
       qr = g_slice_new (QueuedRequest);
       qr->self = g_object_ref (self);
@@ -557,6 +639,10 @@ ide_ctags_service_queue_build_for_directory (IdeCtagsService *self,
                                               g_steal_pointer (&qr),
                                               queued_request_free);
 
+      if (self->paused &&
+          (source = g_main_context_find_source_by_id (NULL, source_id)))
+        g_source_set_ready_time (source, -1);
+
       g_hash_table_insert (self->build_timeout_by_dir,
                            g_object_ref (directory),
                            GUINT_TO_POINTER (source_id));
@@ -569,10 +655,9 @@ ide_ctags_service_buffer_saved (IdeCtagsService  *self,
                                 IdeBufferManager *buffer_manager)
 {
   g_autoptr(GFile) parent = NULL;
+  g_autoptr(GFile) workdir = NULL;
   IdeContext *context;
-  IdeVcs *vcs;
   GFile *file;
-  GFile *workdir;
 
   IDE_ENTRY;
 
@@ -581,35 +666,38 @@ ide_ctags_service_buffer_saved (IdeCtagsService  *self,
   g_assert (IDE_IS_BUFFER_MANAGER (buffer_manager));
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  vcs = ide_context_get_vcs (context);
-  workdir = ide_vcs_get_working_directory (vcs);
+  workdir = ide_context_ref_workdir (context);
 
-  file = ide_file_get_file (ide_buffer_get_file (buffer));
+  file = ide_buffer_get_file (buffer);
   parent = g_file_get_parent (file);
 
-  if (g_file_has_prefix (file, workdir))
+  if (g_file_has_prefix (file, workdir) &&
+      is_supported_language (ide_buffer_get_language_id (buffer)))
     ide_ctags_service_queue_build_for_directory (self, parent, FALSE);
 
   IDE_EXIT;
 }
 
 static void
-ide_ctags_service_context_loaded (IdeService *service)
+ide_ctags_service_parent_set (IdeObject *object,
+                              IdeObject *parent)
 {
-  IdeBufferManager *buffer_manager;
-  IdeCtagsService *self = (IdeCtagsService *)service;
-  IdeContext *context;
-  GFile *workdir;
+  IdeCtagsService *self = (IdeCtagsService *)object;
+  g_autoptr(GFile) workdir = NULL;
+  IdeBufferManager *bufmgr;
 
   IDE_ENTRY;
 
   g_assert (IDE_IS_CTAGS_SERVICE (self));
+  g_assert (!parent || IDE_IS_CONTEXT (parent));
 
-  context = ide_object_get_context (IDE_OBJECT (self));
-  buffer_manager = ide_context_get_buffer_manager (context);
-  workdir = ide_vcs_get_working_directory (ide_context_get_vcs (context));
+  if (parent == NULL)
+    IDE_EXIT;
 
-  g_signal_connect_object (buffer_manager,
+  bufmgr = ide_buffer_manager_from_context (IDE_CONTEXT (parent));
+  workdir = ide_context_ref_workdir (IDE_CONTEXT (parent));
+
+  g_signal_connect_object (bufmgr,
                            "buffer-saved",
                            G_CALLBACK (ide_ctags_service_buffer_saved),
                            self,
@@ -625,21 +713,16 @@ ide_ctags_service_context_loaded (IdeService *service)
 }
 
 static void
-ide_ctags_service_start (IdeService *service)
+ide_ctags_service_destroy (IdeObject *object)
 {
-}
-
-static void
-ide_ctags_service_stop (IdeService *service)
-{
-  IdeCtagsService *self = (IdeCtagsService *)service;
-
-  g_return_if_fail (IDE_IS_CTAGS_SERVICE (self));
+  IdeCtagsService *self = (IdeCtagsService *)object;
 
-  if (self->cancellable && !g_cancellable_is_cancelled (self->cancellable))
-    g_cancellable_cancel (self->cancellable);
+  g_assert (IDE_IS_CTAGS_SERVICE (self));
 
+  g_cancellable_cancel (self->cancellable);
   g_clear_object (&self->cancellable);
+
+  IDE_OBJECT_CLASS (ide_ctags_service_parent_class)->destroy (object);
 }
 
 static void
@@ -661,19 +744,71 @@ ide_ctags_service_finalize (GObject *object)
 }
 
 static void
-ide_ctags_service_class_init (IdeCtagsServiceClass *klass)
+ide_ctags_service_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeCtagsService *self = IDE_CTAGS_SERVICE (object);
 
-  object_class->finalize = ide_ctags_service_finalize;
+  switch (prop_id)
+    {
+    case PROP_PAUSED:
+      g_value_set_boolean (value, self->paused);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
 }
 
 static void
-service_iface_init (IdeServiceInterface *iface)
+ide_ctags_service_set_property (GObject      *object,
+                                guint         prop_id,
+                                const GValue *value,
+                                GParamSpec   *pspec)
 {
-  iface->context_loaded = ide_ctags_service_context_loaded;
-  iface->start = ide_ctags_service_start;
-  iface->stop = ide_ctags_service_stop;
+  IdeCtagsService *self = IDE_CTAGS_SERVICE (object);
+
+  switch (prop_id)
+    {
+    case PROP_PAUSED:
+      if (g_value_get_boolean (value) != self->paused)
+        {
+          if (self->paused)
+            ide_ctags_service_unpause (self);
+          else
+            ide_ctags_service_pause (self);
+        }
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_ctags_service_class_init (IdeCtagsServiceClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeObjectClass *i_object_class = IDE_OBJECT_CLASS (klass);
+
+  object_class->get_property = ide_ctags_service_get_property;
+  object_class->set_property = ide_ctags_service_set_property;
+
+  i_object_class->parent_set = ide_ctags_service_parent_set;
+  i_object_class->destroy = ide_ctags_service_destroy;
+
+  object_class->finalize = ide_ctags_service_finalize;
+
+  properties [PROP_PAUSED] =
+    g_param_spec_boolean ("paused",
+                          "Paused",
+                          "If the service is paused",
+                          FALSE,
+                          (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
 }
 
 static void
@@ -780,3 +915,67 @@ ide_ctags_service_unregister_completion (IdeCtagsService            *self,
 
   g_ptr_array_remove (self->completions, completion);
 }
+
+void
+ide_ctags_service_pause (IdeCtagsService *self)
+{
+  GHashTableIter iter;
+  gpointer key;
+  gpointer value;
+
+  g_return_if_fail (IDE_IS_CTAGS_SERVICE (self));
+
+  if (self->paused)
+    return;
+
+  g_cancellable_cancel (self->cancellable);
+  g_clear_object (&self->cancellable);
+  self->cancellable = g_cancellable_new ();
+
+  self->paused = TRUE;
+
+  /* Make sure we show the pause state so the user can unpause */
+  show_notification (self);
+  ide_notification_set_title (self->notif, _("Indexing Source Code (Paused)"));
+
+  g_hash_table_iter_init (&iter, self->build_timeout_by_dir);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      GSource *source;
+
+      /* Make the source innactive until we are unpaused */
+      if ((source = g_main_context_find_source_by_id (NULL, GPOINTER_TO_UINT (value))))
+        g_source_set_ready_time (source, -1);
+    }
+}
+
+void
+ide_ctags_service_unpause (IdeCtagsService *self)
+{
+  GHashTableIter iter;
+  gpointer key;
+  gpointer value;
+
+  g_return_if_fail (IDE_IS_CTAGS_SERVICE (self));
+
+  if (!self->paused)
+    return;
+
+  self->paused = FALSE;
+
+  g_hash_table_iter_init (&iter, self->build_timeout_by_dir);
+
+  while (g_hash_table_iter_next (&iter, &key, &value))
+    {
+      GSource *source;
+
+      /* Make the source innactive until we are unpaused */
+      if ((source = g_main_context_find_source_by_id (NULL, GPOINTER_TO_UINT (value))))
+        g_source_set_ready_time (source, 0);
+    }
+
+  /* Now we can drop our paused state */
+  ide_notification_set_title (self->notif, _("Indexing Source Code"));
+  hide_notification (self);
+}
diff --git a/src/plugins/ctags/ide-ctags-service.h b/src/plugins/ctags/ide-ctags-service.h
index 2f47e3328..cf273f442 100644
--- a/src/plugins/ctags/ide-ctags-service.h
+++ b/src/plugins/ctags/ide-ctags-service.h
@@ -20,8 +20,7 @@
 
 #pragma once
 
-#include <gtksourceview/gtksource.h>
-#include <ide.h>
+#include <libide-sourceview.h>
 
 #include "ide-ctags-completion-provider.h"
 #include "ide-ctags-highlighter.h"
@@ -32,15 +31,16 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeCtagsService, ide_ctags_service, IDE, CTAGS_SERVICE, IdeObject)
 
-void ide_ctags_service_register_highlighter   (IdeCtagsService            *self,
-                                               IdeCtagsHighlighter        *highlighter);
-void ide_ctags_service_unregister_highlighter (IdeCtagsService            *self,
-                                               IdeCtagsHighlighter        *highlighter);
-void ide_ctags_service_register_completion    (IdeCtagsService            *self,
-                                               IdeCtagsCompletionProvider *completion);
-void ide_ctags_service_unregister_completion  (IdeCtagsService            *self,
-                                               IdeCtagsCompletionProvider *completion);
-
-GPtrArray *ide_ctags_service_get_indexes (IdeCtagsService *self);
+void       ide_ctags_service_register_highlighter   (IdeCtagsService            *self,
+                                                     IdeCtagsHighlighter        *highlighter);
+void       ide_ctags_service_unregister_highlighter (IdeCtagsService            *self,
+                                                     IdeCtagsHighlighter        *highlighter);
+void       ide_ctags_service_register_completion    (IdeCtagsService            *self,
+                                                     IdeCtagsCompletionProvider *completion);
+void       ide_ctags_service_unregister_completion  (IdeCtagsService            *self,
+                                                     IdeCtagsCompletionProvider *completion);
+void       ide_ctags_service_pause                  (IdeCtagsService            *self);
+void       ide_ctags_service_unpause                (IdeCtagsService            *self);
+GPtrArray *ide_ctags_service_get_indexes            (IdeCtagsService            *self);
 
 G_END_DECLS
diff --git a/src/plugins/ctags/ide-ctags-symbol-node.c b/src/plugins/ctags/ide-ctags-symbol-node.c
index d9ea69a62..94a96f1f6 100644
--- a/src/plugins/ctags/ide-ctags-symbol-node.c
+++ b/src/plugins/ctags/ide-ctags-symbol-node.c
@@ -39,7 +39,7 @@ ide_ctags_symbol_node_get_location_cb (GObject      *object,
                                        gpointer      user_data)
 {
   IdeCtagsSymbolResolver *resolver = (IdeCtagsSymbolResolver *)object;
-  g_autoptr(IdeSourceLocation) location = NULL;
+  g_autoptr(IdeLocation) location = NULL;
   g_autoptr(IdeTask) task = user_data;
   g_autoptr(GError) error = NULL;
 
@@ -54,7 +54,7 @@ ide_ctags_symbol_node_get_location_cb (GObject      *object,
   else
     ide_task_return_pointer (task,
                              g_steal_pointer (&location),
-                             (GDestroyNotify)ide_source_location_unref);
+                             (GDestroyNotify)g_object_unref);
 }
 
 static void
@@ -80,7 +80,7 @@ ide_ctags_symbol_node_get_location_async (IdeSymbolNode       *node,
                                                 g_steal_pointer (&task));
 }
 
-static IdeSourceLocation *
+static IdeLocation *
 ide_ctags_symbol_node_get_location_finish (IdeSymbolNode  *node,
                                            GAsyncResult   *result,
                                            GError        **error)
diff --git a/src/plugins/ctags/ide-ctags-symbol-node.h b/src/plugins/ctags/ide-ctags-symbol-node.h
index 038c59909..daea34a55 100644
--- a/src/plugins/ctags/ide-ctags-symbol-node.h
+++ b/src/plugins/ctags/ide-ctags-symbol-node.h
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-code.h>
 
 #include "ide-ctags-index.h"
 #include "ide-ctags-symbol-resolver.h"
diff --git a/src/plugins/ctags/ide-ctags-symbol-resolver.c b/src/plugins/ctags/ide-ctags-symbol-resolver.c
index 1020908de..17d53c3c4 100644
--- a/src/plugins/ctags/ide-ctags-symbol-resolver.c
+++ b/src/plugins/ctags/ide-ctags-symbol-resolver.c
@@ -22,7 +22,7 @@
 
 #include <errno.h>
 #include <glib/gi18n.h>
-#include <ide.h>
+#include <libide-code.h>
 
 #include "ide-ctags-service.h"
 #include "ide-ctags-symbol-node.h"
@@ -74,18 +74,15 @@ create_symbol (IdeCtagsSymbolResolver   *self,
                gint                      line_offset,
                gint                      offset)
 {
-  g_autoptr(IdeSourceLocation) loc = NULL;
+  g_autoptr(IdeLocation) loc = NULL;
   g_autoptr(GFile) gfile = NULL;
-  g_autoptr(IdeFile) file = NULL;
-  IdeContext *context;
 
-  context = ide_object_get_context (IDE_OBJECT (self));
   gfile = g_file_new_for_path (entry->path);
-  file = ide_file_new (context, gfile);
-  loc = ide_source_location_new (file, line, line_offset, offset);
-
-  return ide_symbol_new (entry->name, ide_ctags_index_entry_kind_to_symbol_kind (entry->kind), 0, loc, loc, 
loc);
+  loc = ide_location_new (gfile, line, line_offset);
 
+  return ide_symbol_new (entry->name,
+                         ide_ctags_index_entry_kind_to_symbol_kind (entry->kind),
+                         0, loc, loc);
 }
 
 static gboolean
@@ -245,7 +242,7 @@ regex_worker (IdeTask      *task,
           symbol = create_symbol (self, lookup->entry, line, line_offset, begin);
           ide_task_return_pointer (task,
                                    g_steal_pointer (&symbol),
-                                   (GDestroyNotify)ide_symbol_unref);
+                                   (GDestroyNotify)g_object_unref);
 
           return;
         }
@@ -272,19 +269,18 @@ is_linenum (const gchar *pattern)
 
 static void
 ide_ctags_symbol_resolver_lookup_symbol_async (IdeSymbolResolver   *resolver,
-                                               IdeSourceLocation   *location,
+                                               IdeLocation   *location,
                                                GCancellable        *cancellable,
                                                GAsyncReadyCallback  callback,
                                                gpointer             user_data)
 {
   IdeCtagsSymbolResolver *self = (IdeCtagsSymbolResolver *)resolver;
+  g_autoptr(IdeCtagsService) service = NULL;
   IdeContext *context;
   IdeBufferManager *bufmgr;
-  IdeCtagsService *service;
   g_autofree gchar *keyword = NULL;
   g_autoptr(IdeTask) task = NULL;
   g_autoptr(GPtrArray) indexes = NULL;
-  IdeFile *ifile;
   const gchar * const *allowed;
   const gchar *lang_id = NULL;
   GtkSourceLanguage *language;
@@ -301,16 +297,23 @@ ide_ctags_symbol_resolver_lookup_symbol_async (IdeSymbolResolver   *resolver,
 
   task = ide_task_new (self, cancellable, callback, user_data);
 
-  ifile = ide_source_location_get_file (location);
-  file = ide_file_get_file (ifile);
-  line = ide_source_location_get_line (location);
-  line_offset = ide_source_location_get_line_offset (location);
+  file = ide_location_get_file (location);
+  line = ide_location_get_line (location);
+  line_offset = ide_location_get_line_offset (location);
+
+  if (!(context = ide_object_get_context (IDE_OBJECT (self))) ||
+      !(service = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_CTAGS_SERVICE)))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_SUPPORTED,
+                                 "Service is not loaded. Likely no project was loaded");
+      return;
+    }
 
-  context = ide_object_get_context (IDE_OBJECT (self));
-  service = ide_context_get_service_typed (context, IDE_TYPE_CTAGS_SERVICE);
   indexes = ide_ctags_service_get_indexes (service);
 
-  bufmgr = ide_context_get_buffer_manager (context);
+  bufmgr = ide_buffer_manager_from_context (context);
   buffer = ide_buffer_manager_find_buffer (bufmgr, file);
 
   if (!buffer)
@@ -399,7 +402,7 @@ ide_ctags_symbol_resolver_lookup_symbol_async (IdeSymbolResolver   *resolver,
               symbol = create_symbol (self, entry, parsed, 0, 0);
               ide_task_return_pointer (task,
                                        g_steal_pointer (&symbol),
-                                       (GDestroyNotify)ide_symbol_unref);
+                                       (GDestroyNotify)g_object_unref);
               return;
             }
         }
@@ -641,7 +644,7 @@ ide_ctags_symbol_resolver_get_symbol_tree_worker (IdeTask      *task,
 static void
 ide_ctags_symbol_resolver_get_symbol_tree_async (IdeSymbolResolver   *resolver,
                                                  GFile               *file,
-                                                 IdeBuffer           *buffer,
+                                                 GBytes              *contents,
                                                  GCancellable        *cancellable,
                                                  GAsyncReadyCallback  callback,
                                                  gpointer             user_data)
@@ -650,7 +653,7 @@ ide_ctags_symbol_resolver_get_symbol_tree_async (IdeSymbolResolver   *resolver,
   TreeResolverState *state;
   g_autoptr(IdeTask) task = NULL;
   g_autoptr(GPtrArray) indexes = NULL;
-  IdeCtagsService *service;
+  g_autoptr(IdeCtagsService) service = NULL;
   IdeContext *context;
 
   IDE_ENTRY;
@@ -662,8 +665,16 @@ ide_ctags_symbol_resolver_get_symbol_tree_async (IdeSymbolResolver   *resolver,
   task = ide_task_new (self, cancellable, callback, user_data);
   ide_task_set_source_tag (task, ide_ctags_symbol_resolver_get_symbol_tree_async);
 
-  context = ide_object_get_context (IDE_OBJECT (self));
-  service = ide_context_get_service_typed (context, IDE_TYPE_CTAGS_SERVICE);
+  if (!(context = ide_object_get_context (IDE_OBJECT (self))) ||
+      !(service = ide_object_get_child_typed (IDE_OBJECT (context), IDE_TYPE_CTAGS_SERVICE)))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_SUPPORTED,
+                                 "No ctags service is loaded, likely no project was loaded");
+      return;
+    }
+
   indexes = ide_ctags_service_get_indexes (service);
 
   if (indexes == NULL || indexes->len == 0)
@@ -756,7 +767,7 @@ ide_ctags_symbol_resolver_get_location_async (IdeCtagsSymbolResolver   *self,
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  bufmgr = ide_context_get_buffer_manager (context);
+  bufmgr = ide_buffer_manager_from_context (context);
 
   task = ide_task_new (self, cancellable, callback, user_data);
   ide_task_set_source_tag (task, ide_ctags_symbol_resolver_get_location_async);
@@ -774,7 +785,7 @@ ide_ctags_symbol_resolver_get_location_async (IdeCtagsSymbolResolver   *self,
       symbol = create_symbol (self, entry, parsed, 0, 0);
       ide_task_return_pointer (task,
                                g_steal_pointer (&symbol),
-                               (GDestroyNotify)ide_symbol_unref);
+                               (GDestroyNotify)g_object_unref);
 
       IDE_EXIT;
     }
@@ -818,13 +829,13 @@ not_a_number:
   IDE_EXIT;
 }
 
-IdeSourceLocation *
+IdeLocation *
 ide_ctags_symbol_resolver_get_location_finish (IdeCtagsSymbolResolver  *self,
                                                GAsyncResult            *result,
                                                GError                 **error)
 {
   g_autoptr(IdeSymbol) symbol = NULL;
-  IdeSourceLocation *ret = NULL;
+  IdeLocation *ret = NULL;
 
   g_return_val_if_fail (IDE_IS_CTAGS_SYMBOL_RESOLVER (self), NULL);
   g_return_val_if_fail (IDE_IS_TASK (result), NULL);
@@ -833,8 +844,8 @@ ide_ctags_symbol_resolver_get_location_finish (IdeCtagsSymbolResolver  *self,
 
   if (symbol != NULL)
     {
-      if ((ret = ide_symbol_get_declaration_location (symbol)))
-        ide_source_location_ref (ret);
+      if ((ret = ide_symbol_get_location (symbol)))
+        g_object_ref (ret);
       else
         g_set_error (error,
                      G_IO_ERROR,
diff --git a/src/plugins/ctags/ide-ctags-symbol-resolver.h b/src/plugins/ctags/ide-ctags-symbol-resolver.h
index 816bfd9b1..897a0f40a 100644
--- a/src/plugins/ctags/ide-ctags-symbol-resolver.h
+++ b/src/plugins/ctags/ide-ctags-symbol-resolver.h
@@ -20,7 +20,8 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-core.h>
+#include <libide-code.h>
 
 G_BEGIN_DECLS
 
@@ -34,7 +35,7 @@ void               ide_ctags_symbol_resolver_get_location_async  (IdeCtagsSymbol
                                                                   GCancellable             *cancellable,
                                                                   GAsyncReadyCallback       callback,
                                                                   gpointer                  user_data);
-IdeSourceLocation *ide_ctags_symbol_resolver_get_location_finish (IdeCtagsSymbolResolver   *self,
+IdeLocation *ide_ctags_symbol_resolver_get_location_finish (IdeCtagsSymbolResolver   *self,
                                                                   GAsyncResult             *result,
                                                                   GError                  **error);
 
diff --git a/src/plugins/ctags/ide-ctags-symbol-tree.c b/src/plugins/ctags/ide-ctags-symbol-tree.c
index 003f85032..4c6aae7d1 100644
--- a/src/plugins/ctags/ide-ctags-symbol-tree.c
+++ b/src/plugins/ctags/ide-ctags-symbol-tree.c
@@ -71,8 +71,8 @@ symbol_tree_iface_init (IdeSymbolTreeInterface *iface)
   iface->get_nth_child = ide_ctags_symbol_tree_get_nth_child;
 }
 
-G_DEFINE_TYPE_EXTENDED (IdeCtagsSymbolTree, ide_ctags_symbol_tree, G_TYPE_OBJECT, 0,
-                        G_IMPLEMENT_INTERFACE (IDE_TYPE_SYMBOL_TREE, symbol_tree_iface_init))
+G_DEFINE_TYPE_WITH_CODE (IdeCtagsSymbolTree, ide_ctags_symbol_tree, G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_SYMBOL_TREE, symbol_tree_iface_init))
 
 /**
  * ide_ctags_symbol_tree_new:
diff --git a/src/plugins/ctags/ide-ctags-symbol-tree.h b/src/plugins/ctags/ide-ctags-symbol-tree.h
index 3937d8307..7d0944d51 100644
--- a/src/plugins/ctags/ide-ctags-symbol-tree.h
+++ b/src/plugins/ctags/ide-ctags-symbol-tree.h
@@ -20,7 +20,7 @@
 
 #pragma once
 
-#include <ide.h>
+#include <libide-code.h>
 
 G_BEGIN_DECLS
 
diff --git a/src/plugins/ctags/ide-ctags-util.c b/src/plugins/ctags/ide-ctags-util.c
index 41b943105..1c74a9195 100644
--- a/src/plugins/ctags/ide-ctags-util.c
+++ b/src/plugins/ctags/ide-ctags-util.c
@@ -35,17 +35,17 @@ ide_ctags_get_allowed_suffixes (const gchar *lang_id)
   if (lang_id == NULL)
     return NULL;
 
-  if (dzl_str_equal0 (lang_id, "c") || dzl_str_equal0 (lang_id, "chdr") || dzl_str_equal0 (lang_id, "cpp"))
+  if (ide_str_equal0 (lang_id, "c") || ide_str_equal0 (lang_id, "chdr") || ide_str_equal0 (lang_id, "cpp"))
     return c_languages;
-  else if (dzl_str_equal0 (lang_id, "vala"))
+  else if (ide_str_equal0 (lang_id, "vala"))
     return vala_languages;
-  else if (dzl_str_equal0 (lang_id, "python"))
+  else if (ide_str_equal0 (lang_id, "python"))
     return python_languages;
-  else if (dzl_str_equal0 (lang_id, "js"))
+  else if (ide_str_equal0 (lang_id, "js"))
     return js_languages;
-  else if (dzl_str_equal0 (lang_id, "html"))
+  else if (ide_str_equal0 (lang_id, "html"))
     return html_languages;
-  else if (dzl_str_equal0 (lang_id, "ruby"))
+  else if (ide_str_equal0 (lang_id, "ruby"))
     return ruby_languages;
   else
     return NULL;
@@ -61,7 +61,7 @@ ide_ctags_is_allowed (const IdeCtagsIndexEntry *entry,
       gsize i;
 
       for (i = 0; allowed [i]; i++)
-        if (dzl_str_equal0 (dotptr, allowed [i]))
+        if (ide_str_equal0 (dotptr, allowed [i]))
           return TRUE;
     }
 
diff --git a/src/plugins/ctags/ide-tags-builder.c b/src/plugins/ctags/ide-tags-builder.c
new file mode 100644
index 000000000..e16b8b881
--- /dev/null
+++ b/src/plugins/ctags/ide-tags-builder.c
@@ -0,0 +1,58 @@
+/* ide-tags-builder.c
+ *
+ * Copyright 2015-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 "ide-tags-builder"
+
+#include "config.h"
+
+#include "ide-tags-builder.h"
+
+G_DEFINE_INTERFACE (IdeTagsBuilder, ide_tags_builder, G_TYPE_OBJECT)
+
+
+void
+ide_tags_builder_build_async (IdeTagsBuilder      *self,
+                              GFile               *directory_or_file,
+                              gboolean             recursive,
+                              GCancellable        *cancellable,
+                              GAsyncReadyCallback  callback,
+                              gpointer             user_data)
+{
+  g_return_if_fail (IDE_IS_TAGS_BUILDER (self));
+  g_return_if_fail (!directory_or_file || G_IS_FILE (directory_or_file));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  IDE_TAGS_BUILDER_GET_IFACE (self)->build_async (self, directory_or_file, recursive, cancellable, callback, 
user_data);
+}
+
+gboolean
+ide_tags_builder_build_finish (IdeTagsBuilder  *self,
+                               GAsyncResult    *result,
+                               GError         **error)
+{
+  g_return_val_if_fail (IDE_IS_TAGS_BUILDER (self), FALSE);
+
+  return IDE_TAGS_BUILDER_GET_IFACE (self)->build_finish (self, result, error);
+}
+
+static void
+ide_tags_builder_default_init (IdeTagsBuilderInterface *iface)
+{
+}
diff --git a/src/plugins/ctags/ide-tags-builder.h b/src/plugins/ctags/ide-tags-builder.h
new file mode 100644
index 000000000..ffe390fec
--- /dev/null
+++ b/src/plugins/ctags/ide-tags-builder.h
@@ -0,0 +1,56 @@
+/* ide-tags-builder.h
+ *
+ * Copyright 2015-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 <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TAGS_BUILDER (ide_tags_builder_get_type ())
+
+G_DECLARE_INTERFACE (IdeTagsBuilder, ide_tags_builder, IDE, TAGS_BUILDER, GObject)
+
+struct _IdeTagsBuilderInterface
+{
+  GTypeInterface parent;
+
+  void     (*build_async)  (IdeTagsBuilder       *self,
+                            GFile                *directory_or_file,
+                            gboolean              recursive,
+                            GCancellable         *cancellable,
+                            GAsyncReadyCallback   callback,
+                            gpointer              user_data);
+  gboolean (*build_finish) (IdeTagsBuilder       *self,
+                            GAsyncResult         *result,
+                            GError              **error);
+};
+
+void      ide_tags_builder_build_async  (IdeTagsBuilder       *self,
+                                         GFile                *directory_or_file,
+                                         gboolean              recursive,
+                                         GCancellable         *cancellable,
+                                         GAsyncReadyCallback   callback,
+                                         gpointer              user_data);
+gboolean  ide_tags_builder_build_finish (IdeTagsBuilder       *self,
+                                         GAsyncResult         *result,
+                                         GError              **error);
+
+G_END_DECLS
diff --git a/src/plugins/ctags/meson.build b/src/plugins/ctags/meson.build
index 91c5db33e..acd7fa79a 100644
--- a/src/plugins/ctags/meson.build
+++ b/src/plugins/ctags/meson.build
@@ -1,28 +1,36 @@
-if get_option('with_ctags')
+if get_option('plugin_ctags')
 
-ctags_resources = gnome.compile_resources(
-  'ctags-resources',
-  'ctags.gresource.xml',
-  c_name: 'ide_ctags',
-)
-
-ctags_sources = [
+plugins_sources += files([
+  'ctags-plugin.c',
   'ide-ctags-builder.c',
   'ide-ctags-completion-item.c',
   'ide-ctags-completion-provider.c',
   'ide-ctags-highlighter.c',
   'ide-ctags-index.c',
   'ide-ctags-preferences-addin.c',
-  'ide-ctags-service.c',
   'ide-ctags-results.c',
+  'ide-ctags-service.c',
   'ide-ctags-symbol-node.c',
   'ide-ctags-symbol-resolver.c',
   'ide-ctags-symbol-tree.c',
   'ide-ctags-util.c',
-  'ctags-plugin.c',
-]
+  'ide-tags-builder.c',
+  'gbp-ctags-workbench-addin.c',
+])
+
+plugin_ctags_resources = gnome.compile_resources(
+  'gbp-ctags-resources',
+  'ctags.gresource.xml',
+  c_name: 'gbp_ctags',
+)
+
+plugins_sources += plugin_ctags_resources[0]
 
-gnome_builder_plugins_sources += files(ctags_sources)
-gnome_builder_plugins_sources += ctags_resources[0]
+test_ctags = executable('test-ctags',
+  'test-ctags.c', 'ide-ctags-index.c',
+        c_args: test_cflags,
+  dependencies: [ libide_projects_dep ],
+)
+test('test-ctags', test_ctags, env: test_env)
 
 endif
diff --git a/src/plugins/ctags/test-ctags.c b/src/plugins/ctags/test-ctags.c
new file mode 100644
index 000000000..e3673e790
--- /dev/null
+++ b/src/plugins/ctags/test-ctags.c
@@ -0,0 +1,110 @@
+/* test-ctags.c
+ *
+ * Copyright 2015-2019 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/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <gio/gio.h>
+
+#include "ide-ctags-index.h"
+
+static GMainLoop *main_loop;
+
+static void
+init_cb (GObject      *object,
+         GAsyncResult *result,
+         gpointer      user_data)
+{
+  GAsyncInitable *initable = (GAsyncInitable *)object;
+  IdeCtagsIndex *index = (IdeCtagsIndex *)object;
+  const IdeCtagsIndexEntry *entries;
+  gsize n_entries = 0xFFFFFFFF;
+  GError *error = NULL;
+  gboolean ret;
+  gsize i;
+
+  g_assert (G_IS_ASYNC_INITABLE (initable));
+  g_assert (G_IS_ASYNC_RESULT (result));
+
+  ret = g_async_initable_init_finish (initable, result, &error);
+  g_assert_no_error (error);
+  g_assert_true (ret);
+  g_assert (index != NULL);
+  g_assert (IDE_IS_CTAGS_INDEX (index));
+
+  g_assert_cmpint (28, ==, ide_ctags_index_get_size (index));
+
+  entries = ide_ctags_index_lookup (index, "__NOTHING_SHOULD_MATCH_THIS__", &n_entries);
+  g_assert_cmpint (n_entries, ==, 0);
+  g_assert (entries == NULL);
+
+  entries = ide_ctags_index_lookup (index, "G_LOG_DOMAIN", &n_entries);
+  g_assert_cmpint (n_entries, ==, 1);
+  g_assert (entries != NULL);
+  for (i = 0; i < 1; i++)
+    g_assert_cmpstr (entries [i].name, ==, "G_LOG_DOMAIN");
+
+  entries = ide_ctags_index_lookup (index, "bug_buddy_init", &n_entries);
+  g_assert_cmpint (n_entries, ==, 2);
+  g_assert (entries != NULL);
+  for (i = 0; i < 1; i++)
+    g_assert_cmpstr (entries [i].name, ==, "bug_buddy_init");
+
+  entries = ide_ctags_index_lookup_prefix (index, "G_DEFINE_", &n_entries);
+  g_assert_cmpint (n_entries, ==, 16);
+  g_assert (entries != NULL);
+  for (i = 0; i < 16; i++)
+    g_assert (g_str_has_prefix (entries [i].name, "G_DEFINE_"));
+
+  g_main_loop_quit (main_loop);
+}
+
+static void
+test_ctags_basic (void)
+{
+  IdeCtagsIndex *index;
+  GFile *test_file;
+  gchar *path;
+
+  main_loop = g_main_loop_new (NULL, FALSE);
+
+  path = g_build_filename (TEST_DATA_DIR, "../../plugins/ctags", "test-tags", NULL);
+  test_file = g_file_new_for_path (path);
+
+  index = ide_ctags_index_new (test_file, NULL, 0);
+
+  g_async_initable_init_async (G_ASYNC_INITABLE (index),
+                               G_PRIORITY_DEFAULT,
+                               NULL,
+                               init_cb,
+                               NULL);
+
+  g_main_loop_run (main_loop);
+
+  g_object_unref (index);
+  g_free (path);
+  g_object_unref (test_file);
+}
+
+gint
+main (gint   argc,
+      gchar *argv[])
+{
+  g_test_init (&argc, &argv, NULL);
+  g_test_add_func ("/Ide/CTags/basic", test_ctags_basic);
+  return g_test_run ();
+}
diff --git a/src/plugins/ctags/test-tags b/src/plugins/ctags/test-tags
new file mode 100644
index 000000000..9dd63f71e
--- /dev/null
+++ b/src/plugins/ctags/test-tags
@@ -0,0 +1,28 @@
+G_DEFINE_CONSTRUCTOR   gconstructor.h  25;"    d
+G_DEFINE_CONSTRUCTOR   gconstructor.h  33;"    d
+G_DEFINE_CONSTRUCTOR   gconstructor.h  55;"    d
+G_DEFINE_CONSTRUCTOR   gconstructor.h  80;"    d
+G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA      gconstructor.h  50;"    d
+G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA      gconstructor.h  75;"    d
+G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS       gconstructor.h  53;"    d
+G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS       gconstructor.h  78;"    d
+G_DEFINE_DESTRUCTOR    gconstructor.h  26;"    d
+G_DEFINE_DESTRUCTOR    gconstructor.h  39;"    d
+G_DEFINE_DESTRUCTOR    gconstructor.h  62;"    d
+G_DEFINE_DESTRUCTOR    gconstructor.h  85;"    d
+G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA       gconstructor.h  51;"    d
+G_DEFINE_DESTRUCTOR_NEEDS_PRAGMA       gconstructor.h  76;"    d
+G_DEFINE_DESTRUCTOR_PRAGMA_ARGS        gconstructor.h  60;"    d
+G_DEFINE_DESTRUCTOR_PRAGMA_ARGS        gconstructor.h  83;"    d
+G_HAS_CONSTRUCTORS     gconstructor.h  23;"    d
+G_HAS_CONSTRUCTORS     gconstructor.h  31;"    d
+G_HAS_CONSTRUCTORS     gconstructor.h  47;"    d
+G_HAS_CONSTRUCTORS     gconstructor.h  73;"    d
+G_LOG_DOMAIN   main.c  21;"    d       file:
+bug_buddy_init bug-buddy.c     /^bug_buddy_init (void)$/;"     f
+bug_buddy_init bug-buddy.h     /^void bug_buddy_init (void);$/;"       p
+bug_buddy_sigsegv_handler      bug-buddy.c     /^bug_buddy_sigsegv_handler (int signum)$/;"    f       file:
+early_params_check     main.c  /^early_params_check (gint       *argc,$/;"     f       file:
+gdb_argv       bug-buddy.c     /^static gchar **gdb_argv = NULL;$/;"   v       file:
+main   main.c  /^main (gint   argc,$/;"        f
+verbose_cb     main.c  /^verbose_cb (const gchar  *option_name,$/;"    f       file:


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