[gnome-builder] ctags: add rudimentary IdeSymbolTree backed by ctags



commit dc5b90a5284673f86fc5df98778fc83e00ceb800
Author: Christian Hergert <chergert redhat com>
Date:   Tue Oct 18 20:24:06 2016 -0700

    ctags: add rudimentary IdeSymbolTree backed by ctags
    
    This adds a barebones IdeSymbolTree implementation that is backed by the
    ctags index. There are still a few things that should be improved in this
    code, but generally it works.
    
    Some problems:
    
     - We need to add a IdeSymbolNode::get_location_async() API so that our
       symbol resolver can do more difficult work. With ctags, we might have
       to load the buffer and run a regex across the file to locate the
       symbol location.
    
       At the moment, we do one GIANT hack to pump the main loop while we
       wait for the thread to finish.
    
     - We need to clenaup our symbol resolver so we can reuse the regex
       resolution stuff. We have a bit of duplicated code that could be
       simplified a bit more with some cleaner abstractions.
    
     - Some regexes just don't work today. For example, the regex to locate
       LINE1, etc in the todo plugin doesn't work because the regex doesn't
       actually match. This might just be inconsistencies in regex
       implementations or we need to escape some things.
    
     - It's a bit more object heavy than I'd like, but that is okay I guess.

 plugins/ctags/Makefile.am                 |    4 +
 plugins/ctags/ide-ctags-index.c           |   50 +++-
 plugins/ctags/ide-ctags-index.h           |   44 +++-
 plugins/ctags/ide-ctags-symbol-node.c     |  175 ++++++++++++
 plugins/ctags/ide-ctags-symbol-node.h     |   45 ++++
 plugins/ctags/ide-ctags-symbol-resolver.c |  409 +++++++++++++++++++++++++----
 plugins/ctags/ide-ctags-symbol-resolver.h |   10 +
 plugins/ctags/ide-ctags-symbol-tree.c     |  114 ++++++++
 plugins/ctags/ide-ctags-symbol-tree.h     |   34 +++
 9 files changed, 825 insertions(+), 60 deletions(-)
---
diff --git a/plugins/ctags/Makefile.am b/plugins/ctags/Makefile.am
index 2436c7d..c76e3e7 100644
--- a/plugins/ctags/Makefile.am
+++ b/plugins/ctags/Makefile.am
@@ -18,8 +18,12 @@ libctags_plugin_la_SOURCES = \
        ide-ctags-index.h \
        ide-ctags-service.c \
        ide-ctags-service.h \
+       ide-ctags-symbol-node.c \
+       ide-ctags-symbol-node.h \
        ide-ctags-symbol-resolver.c \
        ide-ctags-symbol-resolver.h \
+       ide-ctags-symbol-tree.c \
+       ide-ctags-symbol-tree.h \
        ide-ctags-util.c \
        ide-ctags-util.h \
        ctags-plugin.c \
diff --git a/plugins/ctags/ide-ctags-index.c b/plugins/ctags/ide-ctags-index.c
index 19445e5..1e97ac3 100644
--- a/plugins/ctags/ide-ctags-index.c
+++ b/plugins/ctags/ide-ctags-index.c
@@ -174,12 +174,11 @@ ide_ctags_index_parse_line (gchar              *line,
       break;
     }
 
-  /* parse key/value pairs like enum:foo8 */
-  while ((iter = forward_to_tab (iter)) &&
-         (iter = forward_to_nontab_and_zero (iter)))
-    {
-      /* TODO: */
-    }
+  /* Store a pointer to the beginning of the key/val pairs */
+  if (NULL != (iter = forward_to_tab (iter)))
+    entry->keyval = iter;
+  else
+    entry->keyval = NULL;
 
   return TRUE;
 }
@@ -639,3 +638,42 @@ ide_ctags_index_get_mtime (IdeCtagsIndex *self)
 
   return self->mtime;
 }
+
+/**
+ * ide_ctags_index_find_with_path:
+ * @self: A #IdeCtagsIndex
+ * @relative_path: A path relative to the indexes base_path.
+ *
+ * This will return a GPtrArray of #IdeCtagsIndex pointers. These
+ * pointers are const and should not be modified or freed.
+ *
+ * The container is owned by the caller and should be freed by the
+ * caller with g_ptr_array_unref().
+ *
+ * Note that this function is not indexed, and therefore is O(n)
+ * running time with `n` is the number of items in the index.
+ *
+ * Returns: (transfer container) (element-type Ide.CtagsIndexEntry): An array
+ *   of items matching the relative path.
+ */
+GPtrArray *
+ide_ctags_index_find_with_path (IdeCtagsIndex *self,
+                                const gchar   *relative_path)
+{
+  GPtrArray *ar;
+
+  g_return_val_if_fail (IDE_IS_CTAGS_INDEX (self), NULL);
+  g_return_val_if_fail (relative_path != NULL, NULL);
+
+  ar = g_ptr_array_new ();
+
+  for (guint i = 0; i < self->index->len; i++)
+    {
+      IdeCtagsIndexEntry *entry = &g_array_index (self->index, IdeCtagsIndexEntry, i);
+
+      if (g_str_equal (entry->path, relative_path))
+        g_ptr_array_add (ar, entry);
+    }
+
+  return ar;
+}
diff --git a/plugins/ctags/ide-ctags-index.h b/plugins/ctags/ide-ctags-index.h
index 476fbde..c21a70d 100644
--- a/plugins/ctags/ide-ctags-index.h
+++ b/plugins/ctags/ide-ctags-index.h
@@ -20,8 +20,7 @@
 #define IDE_CTAGS_INDEX_H
 
 #include <gio/gio.h>
-
-#include "ide-object.h"
+#include <ide.h>
 
 G_BEGIN_DECLS
 
@@ -51,6 +50,7 @@ typedef struct
   const gchar            *name;
   const gchar            *path;
   const gchar            *pattern;
+  const gchar            *keyval;
   IdeCtagsIndexEntryKind  kind : 8;
   guint8                  padding[3];
 } IdeCtagsIndexEntry;
@@ -66,6 +66,8 @@ void                      ide_ctags_index_load_async    (IdeCtagsIndex
 gboolean                  ide_ctags_index_load_finish   (IdeCtagsIndex            *index,
                                                          GAsyncResult             *result,
                                                          GError                  **error);
+GPtrArray                *ide_ctags_index_find_with_path(IdeCtagsIndex           *self,
+                                                         const gchar             *relative_path);
 gchar                    *ide_ctags_index_resolve_path  (IdeCtagsIndex            *self,
                                                          const gchar              *path);
 GFile                    *ide_ctags_index_get_file      (IdeCtagsIndex            *self);
@@ -83,6 +85,44 @@ gint                      ide_ctags_index_entry_compare (gconstpointer
 IdeCtagsIndexEntry       *ide_ctags_index_entry_copy    (const IdeCtagsIndexEntry *entry);
 void                      ide_ctags_index_entry_free    (IdeCtagsIndexEntry       *entry);
 
+static inline IdeSymbolKind
+ide_ctags_index_entry_kind_to_symbol_kind (IdeCtagsIndexEntryKind kind)
+{
+  switch (kind)
+    {
+    case IDE_CTAGS_INDEX_ENTRY_TYPEDEF:
+    case IDE_CTAGS_INDEX_ENTRY_PROTOTYPE:
+      /* bit of an impedenece mismatch */
+    case IDE_CTAGS_INDEX_ENTRY_CLASS_NAME:
+      return IDE_SYMBOL_CLASS;
+
+    case IDE_CTAGS_INDEX_ENTRY_ENUMERATOR:
+      return IDE_SYMBOL_ENUM;
+
+    case IDE_CTAGS_INDEX_ENTRY_ENUMERATION_NAME:
+      return IDE_SYMBOL_ENUM_VALUE;
+
+    case IDE_CTAGS_INDEX_ENTRY_FUNCTION:
+    case IDE_CTAGS_INDEX_ENTRY_MEMBER:
+      return IDE_SYMBOL_FUNCTION;
+
+    case IDE_CTAGS_INDEX_ENTRY_STRUCTURE:
+      return IDE_SYMBOL_STRUCT;
+
+    case IDE_CTAGS_INDEX_ENTRY_UNION:
+      return IDE_SYMBOL_UNION;
+
+    case IDE_CTAGS_INDEX_ENTRY_VARIABLE:
+      return IDE_SYMBOL_VARIABLE;
+
+    case IDE_CTAGS_INDEX_ENTRY_ANCHOR:
+    case IDE_CTAGS_INDEX_ENTRY_DEFINE:
+    case IDE_CTAGS_INDEX_ENTRY_FILE_NAME:
+    default:
+      return IDE_SYMBOL_NONE;
+    }
+}
+
 G_END_DECLS
 
 #endif /* IDE_CTAGS_INDEX_H */
diff --git a/plugins/ctags/ide-ctags-symbol-node.c b/plugins/ctags/ide-ctags-symbol-node.c
new file mode 100644
index 0000000..97ad7cd
--- /dev/null
+++ b/plugins/ctags/ide-ctags-symbol-node.c
@@ -0,0 +1,175 @@
+/* ide-ctags-symbol-node.c
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#define G_LOG_DOMAIN "ide-ctags-symbol-node"
+
+#include "ide-ctags-symbol-node.h"
+
+struct _IdeCtagsSymbolNode
+{
+  IdeSymbolNode             parent_instance;
+  IdeCtagsIndex            *index;
+  IdeCtagsSymbolResolver   *resolver;
+  const IdeCtagsIndexEntry *entry;
+  GPtrArray                *children;
+};
+
+typedef struct
+{
+  IdeSourceLocation *loc;
+  gboolean done;
+} Completion;
+
+G_DEFINE_TYPE (IdeCtagsSymbolNode, ide_ctags_symbol_node, IDE_TYPE_SYMBOL_NODE)
+
+static void
+handle_location_cb (GObject      *object,
+                    GAsyncResult *result,
+                    gpointer      user_data)
+{
+  IdeCtagsSymbolResolver *resolver = (IdeCtagsSymbolResolver *)object;
+  Completion *comp = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_CTAGS_SYMBOL_RESOLVER (resolver));
+  g_assert (comp != NULL);
+  g_assert (comp->loc == NULL);
+  g_assert (comp->done == FALSE);
+
+  comp->loc = ide_ctags_symbol_resolver_get_location_finish (resolver, result, &error);
+  comp->done = TRUE;
+
+  if (error != NULL)
+    g_warning ("%s", error->message);
+}
+
+static IdeSourceLocation *
+ide_ctags_symbol_node_get_location (IdeSymbolNode *node)
+{
+  IdeCtagsSymbolNode *self = (IdeCtagsSymbolNode *)node;
+  Completion comp = { 0 };
+
+  g_return_val_if_fail (IDE_IS_CTAGS_SYMBOL_NODE (self), NULL);
+
+  ide_ctags_symbol_resolver_get_location_async (self->resolver,
+                                                self->index,
+                                                self->entry,
+                                                NULL,
+                                                handle_location_cb,
+                                                &comp);
+
+  /* XXX: FIXME: TODO: ULTRA: Hack until we add async get_location() API */
+
+  while (!comp.done)
+    gtk_main_iteration ();
+
+  return comp.loc;
+}
+
+static void
+ide_ctags_symbol_node_finalize (GObject *object)
+{
+  IdeCtagsSymbolNode *self = (IdeCtagsSymbolNode *)object;
+
+  g_clear_pointer (&self->children, g_ptr_array_unref);
+  self->entry = NULL;
+  g_clear_object (&self->index);
+
+  G_OBJECT_CLASS (ide_ctags_symbol_node_parent_class)->finalize (object);
+}
+
+static void
+ide_ctags_symbol_node_class_init (IdeCtagsSymbolNodeClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeSymbolNodeClass *symbol_node_class = IDE_SYMBOL_NODE_CLASS (klass);
+
+  object_class->finalize = ide_ctags_symbol_node_finalize;
+
+  symbol_node_class->get_location = ide_ctags_symbol_node_get_location;
+}
+
+static void
+ide_ctags_symbol_node_init (IdeCtagsSymbolNode *self)
+{
+}
+
+IdeCtagsSymbolNode *
+ide_ctags_symbol_node_new (IdeCtagsSymbolResolver   *resolver,
+                           IdeCtagsIndex            *index,
+                           const IdeCtagsIndexEntry *entry)
+{
+  IdeCtagsSymbolNode *self;
+  IdeSymbolFlags flags = IDE_SYMBOL_FLAGS_NONE;
+
+  g_assert (IDE_IS_CTAGS_SYMBOL_RESOLVER (resolver));
+  g_assert (IDE_IS_CTAGS_INDEX (index));
+  g_assert (entry != NULL);
+
+  self = g_object_new (IDE_TYPE_CTAGS_SYMBOL_NODE,
+                       "name", entry->name,
+                       "kind", ide_ctags_index_entry_kind_to_symbol_kind (entry->kind),
+                       "flags", flags,
+                       NULL);
+
+  self->entry = entry;
+  self->index = g_object_ref (index);
+  self->resolver = g_object_ref (resolver);
+
+  return self;
+}
+
+guint
+ide_ctags_symbol_node_get_n_children (IdeCtagsSymbolNode *self)
+{
+  g_return_val_if_fail (IDE_IS_CTAGS_SYMBOL_NODE (self), 0);
+
+  return self->children != NULL ? self->children->len : 0;
+}
+
+IdeSymbolNode *
+ide_ctags_symbol_node_get_nth_child (IdeCtagsSymbolNode *self,
+                                     guint               nth_child)
+{
+  g_return_val_if_fail (IDE_IS_CTAGS_SYMBOL_NODE (self), NULL);
+
+  if (self->children != NULL && nth_child < self->children->len)
+    return g_object_ref (g_ptr_array_index (self->children, nth_child));
+
+  return NULL;
+}
+
+void
+ide_ctags_symbol_node_take_child (IdeCtagsSymbolNode *self,
+                                  IdeCtagsSymbolNode *child)
+{
+  g_return_if_fail (IDE_IS_CTAGS_SYMBOL_NODE (self));
+  g_return_if_fail (IDE_IS_CTAGS_SYMBOL_NODE (child));
+
+  if (self->children == NULL)
+    self->children = g_ptr_array_new_with_free_func (g_object_unref);
+  g_ptr_array_add (self->children, child);
+}
+
+const IdeCtagsIndexEntry *
+ide_ctags_symbol_node_get_entry (IdeCtagsSymbolNode *self)
+{
+  g_return_val_if_fail (IDE_IS_CTAGS_SYMBOL_NODE (self), NULL);
+
+  return self->entry;
+}
diff --git a/plugins/ctags/ide-ctags-symbol-node.h b/plugins/ctags/ide-ctags-symbol-node.h
new file mode 100644
index 0000000..0f07881
--- /dev/null
+++ b/plugins/ctags/ide-ctags-symbol-node.h
@@ -0,0 +1,45 @@
+/* ide-ctags-symbol-node.h
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#ifndef IDE_CTAGS_SYMBOL_NODE_H
+#define IDE_CTAGS_SYMBOL_NODE_H
+
+#include <ide.h>
+
+#include "ide-ctags-index.h"
+#include "ide-ctags-symbol-resolver.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_CTAGS_SYMBOL_NODE (ide_ctags_symbol_node_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeCtagsSymbolNode, ide_ctags_symbol_node, IDE, CTAGS_SYMBOL_NODE, IdeSymbolNode)
+
+IdeCtagsSymbolNode       *ide_ctags_symbol_node_new            (IdeCtagsSymbolResolver   *resolver,
+                                                                IdeCtagsIndex            *index,
+                                                                const IdeCtagsIndexEntry *entry);
+void                      ide_ctags_symbol_node_take_child     (IdeCtagsSymbolNode       *self,
+                                                                IdeCtagsSymbolNode       *child);
+guint                     ide_ctags_symbol_node_get_n_children (IdeCtagsSymbolNode       *self);
+IdeSymbolNode            *ide_ctags_symbol_node_get_nth_child  (IdeCtagsSymbolNode       *self,
+                                                                guint                     nth_child);
+const IdeCtagsIndexEntry *ide_ctags_symbol_node_get_entry      (IdeCtagsSymbolNode       *self);
+
+G_END_DECLS
+
+#endif /* IDE_CTAGS_SYMBOL_NODE_H */
diff --git a/plugins/ctags/ide-ctags-symbol-resolver.c b/plugins/ctags/ide-ctags-symbol-resolver.c
index 0e9b8a5..0f95196 100644
--- a/plugins/ctags/ide-ctags-symbol-resolver.c
+++ b/plugins/ctags/ide-ctags-symbol-resolver.c
@@ -20,11 +20,12 @@
 
 #include <errno.h>
 #include <glib/gi18n.h>
-
-#include "ide-internal.h"
+#include <ide.h>
 
 #include "ide-ctags-service.h"
+#include "ide-ctags-symbol-node.h"
 #include "ide-ctags-symbol-resolver.h"
+#include "ide-ctags-symbol-tree.h"
 #include "ide-ctags-util.h"
 
 struct _IdeCtagsSymbolResolver
@@ -65,46 +66,6 @@ lookup_symbol_free (gpointer data)
   g_slice_free (LookupSymbol, lookup);
 }
 
-static IdeSymbolKind
-transform_kind (IdeCtagsIndexEntryKind kind)
-{
-  switch (kind)
-    {
-    case IDE_CTAGS_INDEX_ENTRY_TYPEDEF:
-    case IDE_CTAGS_INDEX_ENTRY_PROTOTYPE:
-      /* bit of an impedenece mismatch */
-    case IDE_CTAGS_INDEX_ENTRY_CLASS_NAME:
-      return IDE_SYMBOL_CLASS;
-
-    case IDE_CTAGS_INDEX_ENTRY_ENUMERATOR:
-      return IDE_SYMBOL_ENUM;
-
-    case IDE_CTAGS_INDEX_ENTRY_ENUMERATION_NAME:
-      return IDE_SYMBOL_ENUM_VALUE;
-
-    case IDE_CTAGS_INDEX_ENTRY_FUNCTION:
-      return IDE_SYMBOL_FUNCTION;
-
-    case IDE_CTAGS_INDEX_ENTRY_MEMBER:
-      return IDE_SYMBOL_FIELD;
-
-    case IDE_CTAGS_INDEX_ENTRY_STRUCTURE:
-      return IDE_SYMBOL_STRUCT;
-
-    case IDE_CTAGS_INDEX_ENTRY_UNION:
-      return IDE_SYMBOL_UNION;
-
-    case IDE_CTAGS_INDEX_ENTRY_VARIABLE:
-      return IDE_SYMBOL_VARIABLE;
-
-    case IDE_CTAGS_INDEX_ENTRY_ANCHOR:
-    case IDE_CTAGS_INDEX_ENTRY_DEFINE:
-    case IDE_CTAGS_INDEX_ENTRY_FILE_NAME:
-    default:
-      return IDE_SYMBOL_NONE;
-    }
-}
-
 static IdeSymbol *
 create_symbol (IdeCtagsSymbolResolver   *self,
                const IdeCtagsIndexEntry *entry,
@@ -125,7 +86,7 @@ create_symbol (IdeCtagsSymbolResolver   *self,
                        NULL);
   loc = ide_source_location_new (file, line, line_offset, offset);
 
-  return ide_symbol_new (entry->name, transform_kind (entry->kind), 0, loc, loc, loc);
+  return ide_symbol_new (entry->name, ide_ctags_index_entry_kind_to_symbol_kind (entry->kind), 0, loc, loc, 
loc);
 
 }
 
@@ -447,6 +408,210 @@ ide_ctags_symbol_resolver_lookup_symbol_finish (IdeSymbolResolver  *resolver,
   return g_task_propagate_pointer (task, error);
 }
 
+typedef struct
+{
+  GPtrArray *indexes;
+  GFile *file;
+} TreeResolverState;
+
+static void
+tree_resolver_state_free (gpointer data)
+{
+  TreeResolverState *state = data;
+
+  if (state != NULL)
+    {
+      g_clear_pointer (&state->indexes, g_ptr_array_unref);
+      g_clear_object (&state->file);
+      g_slice_free (TreeResolverState, state);
+    }
+}
+
+static gboolean
+maybe_attach_to_parent (IdeCtagsSymbolNode       *node,
+                        const IdeCtagsIndexEntry *entry,
+                        GHashTable               *parents)
+{
+  g_assert (IDE_IS_CTAGS_SYMBOL_NODE (node));
+  g_assert (parents != NULL);
+
+  if (entry->keyval != NULL)
+    {
+      g_auto(GStrv) parts = g_strsplit (entry->keyval, "\t", 0);
+
+      for (guint i = 0; parts[i] != NULL; i++)
+        {
+          IdeCtagsSymbolNode *parent;
+
+          if (NULL != (parent = g_hash_table_lookup (parents, parts[i])))
+            {
+              ide_ctags_symbol_node_take_child (parent, node);
+              return TRUE;
+            }
+        }
+    }
+
+  return FALSE;
+}
+
+static gchar *
+make_parent_key (const IdeCtagsIndexEntry *entry)
+{
+  switch (entry->kind)
+    {
+    case IDE_CTAGS_INDEX_ENTRY_CLASS_NAME:
+      return g_strdup_printf ("class:%s", entry->name);
+
+    case IDE_CTAGS_INDEX_ENTRY_UNION:
+      return g_strdup_printf ("union:%s", entry->name);
+
+    case IDE_CTAGS_INDEX_ENTRY_STRUCTURE:
+      return g_strdup_printf ("struct:%s", entry->name);
+
+    case IDE_CTAGS_INDEX_ENTRY_FUNCTION:
+    case IDE_CTAGS_INDEX_ENTRY_MEMBER:
+      {
+        const gchar *colon;
+
+        /*
+         * If there is a keyval (like class:foo, then we strip the
+         * key, and make a key like type:parent.name.
+         */
+        if (entry->keyval && NULL != (colon = strchr (entry->keyval, ':')))
+          return g_strdup_printf ("function:%s.%s", colon + 1, entry->name);
+        return g_strdup_printf ("function:%s", entry->name);
+      }
+
+    case IDE_CTAGS_INDEX_ENTRY_ENUMERATION_NAME:
+      return g_strdup_printf ("enum:%s", entry->name);
+
+    case IDE_CTAGS_INDEX_ENTRY_VARIABLE:
+    case IDE_CTAGS_INDEX_ENTRY_PROTOTYPE:
+    case IDE_CTAGS_INDEX_ENTRY_DEFINE:
+    case IDE_CTAGS_INDEX_ENTRY_TYPEDEF:
+    case IDE_CTAGS_INDEX_ENTRY_FILE_NAME:
+    case IDE_CTAGS_INDEX_ENTRY_ANCHOR:
+    case IDE_CTAGS_INDEX_ENTRY_ENUMERATOR:
+    default:
+      break;
+    }
+
+  return NULL;
+}
+
+static void
+ide_ctags_symbol_resolver_get_symbol_tree_worker (GTask        *task,
+                                                  gpointer      source_object,
+                                                  gpointer      task_data,
+                                                  GCancellable *cancellable)
+{
+  IdeCtagsSymbolResolver *self = source_object;
+  TreeResolverState *state = task_data;
+  g_autoptr(GPtrArray) ar = NULL;
+  g_autoptr(GFile) parent = NULL;
+  g_autofree gchar *parent_path = NULL;
+
+  g_assert (IDE_IS_CTAGS_SYMBOL_RESOLVER (self));
+  g_assert (G_IS_TASK (task));
+  g_assert (state != NULL);
+  g_assert (G_IS_FILE (state->file));
+  g_assert (state->indexes != NULL);
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  parent = g_file_get_parent (state->file);
+  parent_path = g_file_get_path (parent);
+
+  /*
+   * Ctags does not give us enough information to create a tree, so our
+   * symbols will not have a hierarchy.
+   */
+  ar = g_ptr_array_new_with_free_func (g_object_unref);
+
+  for (guint i = 0; i < state->indexes->len; i++)
+    {
+      IdeCtagsIndex *index = g_ptr_array_index (state->indexes, i);
+      const gchar *base_path = ide_ctags_index_get_path_root (index);
+      g_autoptr(GFile) base_dir = NULL;
+      g_autoptr(GPtrArray) entries = NULL;
+      g_autofree gchar *relative_path = NULL;
+      g_autoptr(GHashTable) keymap = NULL;
+      g_autoptr(GPtrArray) tmp = NULL;
+
+      if (!g_str_has_prefix (parent_path, base_path))
+        continue;
+
+      base_dir = g_file_new_for_path (base_path);
+      relative_path = g_file_get_relative_path (base_dir, state->file);
+
+      /* Shouldn't happen, but be safe */
+      if G_UNLIKELY (relative_path == NULL)
+        continue;
+
+      /* We use keymap to find the parent for things like class:Foo */
+      keymap = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+      entries = ide_ctags_index_find_with_path (index, relative_path);
+      tmp = g_ptr_array_new ();
+
+      /*
+       * We have to build the items in two steps incase the parent for
+       * an item comes after the child. Once we have the parent names
+       * inflated into the hashtable, we can resolve them and build the
+       * final tree.
+       */
+
+      for (guint j = 0; j < entries->len; j++)
+        {
+          const IdeCtagsIndexEntry *entry = g_ptr_array_index (entries, j);
+          g_autoptr(IdeCtagsSymbolNode) node = NULL;
+
+          switch (entry->kind)
+            {
+            case IDE_CTAGS_INDEX_ENTRY_CLASS_NAME:
+            case IDE_CTAGS_INDEX_ENTRY_UNION:
+            case IDE_CTAGS_INDEX_ENTRY_STRUCTURE:
+            case IDE_CTAGS_INDEX_ENTRY_TYPEDEF:
+            case IDE_CTAGS_INDEX_ENTRY_MEMBER:
+            case IDE_CTAGS_INDEX_ENTRY_FUNCTION:
+            case IDE_CTAGS_INDEX_ENTRY_VARIABLE:
+            case IDE_CTAGS_INDEX_ENTRY_PROTOTYPE:
+            case IDE_CTAGS_INDEX_ENTRY_DEFINE:
+            case IDE_CTAGS_INDEX_ENTRY_ENUMERATION_NAME:
+              node = ide_ctags_symbol_node_new (self, index, entry);
+              break;
+
+            case IDE_CTAGS_INDEX_ENTRY_FILE_NAME:
+            case IDE_CTAGS_INDEX_ENTRY_ANCHOR:
+            case IDE_CTAGS_INDEX_ENTRY_ENUMERATOR:
+            default:
+              break;
+            }
+
+          if (node != NULL)
+            {
+              gchar *key = make_parent_key (entry);
+              if (key != NULL)
+                g_hash_table_insert (keymap, key, node);
+              g_ptr_array_add (tmp, g_steal_pointer (&node));
+            }
+        }
+
+      /*
+       * Now go resolve parents and build the tree.
+       */
+
+      for (guint j = 0; j < tmp->len; j++)
+        {
+          IdeCtagsSymbolNode *node = g_ptr_array_index (tmp, j);
+          const IdeCtagsIndexEntry *entry = ide_ctags_symbol_node_get_entry (node);
+
+          if (!maybe_attach_to_parent (node, entry, keymap))
+            g_ptr_array_add (ar, node);
+        }
+    }
+
+  g_task_return_pointer (task, ide_ctags_symbol_tree_new (g_steal_pointer (&ar)), g_object_unref);
+}
+
 static void
 ide_ctags_symbol_resolver_get_symbol_tree_async (IdeSymbolResolver   *resolver,
                                                  GFile               *file,
@@ -455,21 +620,53 @@ ide_ctags_symbol_resolver_get_symbol_tree_async (IdeSymbolResolver   *resolver,
                                                  gpointer             user_data)
 {
   IdeCtagsSymbolResolver *self = (IdeCtagsSymbolResolver *)resolver;
+  TreeResolverState *state;
   g_autoptr(GTask) task = NULL;
+  g_autoptr(GPtrArray) indexes = NULL;
+  IdeCtagsService *service;
+  IdeContext *context;
+
+  IDE_ENTRY;
 
   g_assert (IDE_IS_CTAGS_SYMBOL_RESOLVER (self));
   g_assert (G_IS_FILE (file));
   g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
 
+  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);
+
+  if (indexes == NULL || indexes->len == 0)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_SUPPORTED,
+                               "No ctags indexes are loaded");
+      return;
+    }
+
+  state = g_slice_new0 (TreeResolverState);
+  state->file = g_object_ref (file);
+  state->indexes = g_ptr_array_new_with_free_func (g_object_unref);
+
   /*
-   * FIXME: I think the symbol tree should be a separate interface.
+   * We make a copy of the indexes so that we can access them in a thread.
+   * The container is not mutable, but the indexes are thread safe in that
+   * they don't do mutation after creation.
    */
+  for (guint i = 0; i < indexes->len; i++)
+    {
+      IdeCtagsIndex *index = g_ptr_array_index (indexes, i);
+
+      g_ptr_array_add (state->indexes, g_object_ref (index));
+    }
 
   task = g_task_new (self, cancellable, callback, user_data);
-  g_task_return_new_error (task,
-                           G_IO_ERROR,
-                           G_IO_ERROR_NOT_SUPPORTED,
-                           "CTags symbol resolver does not support symbol tree.");
+  g_task_set_task_data (task, state, tree_resolver_state_free);
+  g_task_set_source_tag (task, ide_ctags_symbol_resolver_get_symbol_tree_async);
+  g_task_run_in_thread (task, ide_ctags_symbol_resolver_get_symbol_tree_worker);
+
+  IDE_EXIT;
 }
 
 static IdeSymbolTree *
@@ -477,12 +674,10 @@ ide_ctags_symbol_resolver_get_symbol_tree_finish (IdeSymbolResolver  *resolver,
                                                   GAsyncResult       *result,
                                                   GError            **error)
 {
-  GTask *task = (GTask *)result;
-
   g_assert (IDE_IS_CTAGS_SYMBOL_RESOLVER (resolver));
-  g_assert (G_IS_TASK (task));
+  g_assert (G_IS_TASK (result));
 
-  return g_task_propagate_pointer (task, error);
+  return g_task_propagate_pointer (G_TASK (result), error);
 }
 
 static void
@@ -514,3 +709,113 @@ _ide_ctags_symbol_resolver_register_type (GTypeModule *module)
 {
   ide_ctags_symbol_resolver_register_type (module);
 }
+
+void
+ide_ctags_symbol_resolver_get_location_async (IdeCtagsSymbolResolver   *self,
+                                              IdeCtagsIndex            *index,
+                                              const IdeCtagsIndexEntry *entry,
+                                              GCancellable             *cancellable,
+                                              GAsyncReadyCallback       callback,
+                                              gpointer                  user_data)
+{
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(GFile) other_file = NULL;
+  IdeBuffer *other_buffer = NULL;
+  IdeCtagsIndexEntry *copy;
+  IdeBufferManager *bufmgr;
+  LookupSymbol *lookup;
+  IdeContext *context;
+
+  IDE_ENTRY;
+
+  g_return_if_fail (IDE_IS_CTAGS_SYMBOL_RESOLVER (self));
+  g_return_if_fail (entry != NULL);
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  bufmgr = ide_context_get_buffer_manager (context);
+
+  task = g_task_new (self, cancellable, callback, user_data);
+  g_task_set_source_tag (task, ide_ctags_symbol_resolver_get_location_async);
+
+  if (is_linenum (entry->pattern))
+    {
+      g_autoptr(IdeSymbol) symbol = NULL;
+      gint64 parsed;
+
+      parsed = g_ascii_strtoll (entry->pattern, NULL, 10);
+
+      if (((parsed == 0) && (errno == ERANGE)) || (parsed > G_MAXINT) || (parsed < 0))
+        goto not_a_number;
+
+      symbol = create_symbol (self, entry, parsed, 0, 0);
+      g_task_return_pointer (task, symbol, (GDestroyNotify)ide_symbol_unref);
+
+      IDE_EXIT;
+    }
+
+not_a_number:
+
+  if (!is_regex (entry->pattern))
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_NOT_SUPPORTED,
+                               "Failed to decode jump in ctag entry");
+      IDE_EXIT;
+    }
+
+  /*
+   * Adjust the filename in our copy to be the full path.
+   * Sort of grabbing at internals here, but hey, we are
+   * our own plugin.
+   */
+  copy = ide_ctags_index_entry_copy (entry);
+  g_free ((gchar *)copy->path);
+  copy->path = ide_ctags_index_resolve_path (index, entry->path);
+
+  lookup = g_slice_new0 (LookupSymbol);
+  lookup->entry = copy;
+
+  other_file = g_file_new_for_path (copy->path);
+
+  if (NULL != (other_buffer = ide_buffer_manager_find_buffer (bufmgr, other_file)))
+    {
+      GtkTextIter begin, end;
+
+      gtk_text_buffer_get_bounds (GTK_TEXT_BUFFER (other_buffer), &begin, &end);
+      lookup->buffer_text = gtk_text_iter_get_slice (&begin, &end);
+    }
+
+  g_task_set_task_data (task, lookup, lookup_symbol_free);
+  g_task_run_in_thread (task, regex_worker);
+
+  IDE_EXIT;
+}
+
+IdeSourceLocation *
+ide_ctags_symbol_resolver_get_location_finish (IdeCtagsSymbolResolver  *self,
+                                               GAsyncResult            *result,
+                                               GError                 **error)
+{
+  g_autoptr(IdeSymbol) symbol = NULL;
+  IdeSourceLocation *ret = NULL;
+
+  g_return_val_if_fail (IDE_IS_CTAGS_SYMBOL_RESOLVER (self), NULL);
+  g_return_val_if_fail (G_IS_TASK (result), NULL);
+
+  symbol = g_task_propagate_pointer (G_TASK (result), error);
+
+  if (symbol != NULL)
+    {
+      if (NULL != (ret = ide_symbol_get_declaration_location (symbol)))
+        ide_source_location_ref (ret);
+      else
+        g_set_error (error,
+                     G_IO_ERROR,
+                     G_IO_ERROR_NOT_FOUND,
+                     "Failed to locate symbol location");
+    }
+
+  return ret;
+}
diff --git a/plugins/ctags/ide-ctags-symbol-resolver.h b/plugins/ctags/ide-ctags-symbol-resolver.h
index efca437..94e8c3b 100644
--- a/plugins/ctags/ide-ctags-symbol-resolver.h
+++ b/plugins/ctags/ide-ctags-symbol-resolver.h
@@ -27,6 +27,16 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeCtagsSymbolResolver, ide_ctags_symbol_resolver, IDE, CTAGS_SYMBOL_RESOLVER, 
IdeObject)
 
+void               ide_ctags_symbol_resolver_get_location_async  (IdeCtagsSymbolResolver   *self,
+                                                                  IdeCtagsIndex            *index,
+                                                                  const IdeCtagsIndexEntry *entry,
+                                                                  GCancellable             *cancellable,
+                                                                  GAsyncReadyCallback       callback,
+                                                                  gpointer                  user_data);
+IdeSourceLocation *ide_ctags_symbol_resolver_get_location_finish (IdeCtagsSymbolResolver   *self,
+                                                                  GAsyncResult             *result,
+                                                                  GError                  **error);
+
 G_END_DECLS
 
 #endif /* IDE_CTAGS_SYMBOL_RESOLVER_H */
diff --git a/plugins/ctags/ide-ctags-symbol-tree.c b/plugins/ctags/ide-ctags-symbol-tree.c
new file mode 100644
index 0000000..c534f46
--- /dev/null
+++ b/plugins/ctags/ide-ctags-symbol-tree.c
@@ -0,0 +1,114 @@
+/* ide-ctags-symbol-tree.c
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#define G_LOG_DOMAIN "ide-ctags-symbol-tree"
+
+#include "ide-ctags-symbol-node.h"
+#include "ide-ctags-symbol-tree.h"
+
+struct _IdeCtagsSymbolTree
+{
+  GObject parent_instance;
+  GPtrArray *ar;
+};
+
+static guint
+ide_ctags_symbol_tree_get_n_children (IdeSymbolTree *tree,
+                                      IdeSymbolNode *node)
+{
+  IdeCtagsSymbolTree *self = (IdeCtagsSymbolTree *)tree;
+
+  g_assert (IDE_IS_CTAGS_SYMBOL_TREE (tree));
+  g_assert (!node || IDE_IS_CTAGS_SYMBOL_NODE (node));
+
+  if (node == NULL)
+    return self->ar->len;
+
+  return ide_ctags_symbol_node_get_n_children (IDE_CTAGS_SYMBOL_NODE (node));
+}
+
+static IdeSymbolNode *
+ide_ctags_symbol_tree_get_nth_child (IdeSymbolTree *tree,
+                                     IdeSymbolNode *node,
+                                     guint          nth)
+{
+  IdeCtagsSymbolTree *self = (IdeCtagsSymbolTree *)tree;
+
+  g_assert (IDE_IS_CTAGS_SYMBOL_TREE (tree));
+  g_assert (!node || IDE_IS_CTAGS_SYMBOL_NODE (node));
+
+  if (node == NULL)
+    {
+      if (nth < self->ar->len)
+        return g_object_ref (g_ptr_array_index (self->ar, nth));
+      return NULL;
+    }
+
+  return ide_ctags_symbol_node_get_nth_child (IDE_CTAGS_SYMBOL_NODE (node), nth);
+}
+
+static void
+symbol_tree_iface_init (IdeSymbolTreeInterface *iface)
+{
+  iface->get_n_children = ide_ctags_symbol_tree_get_n_children;
+  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))
+
+/**
+ * ide_ctags_symbol_tree_new:
+ * @ar: An array of #IdeSymbol instances
+ *
+ * This function takes ownership of @ar.
+ *
+ */
+IdeCtagsSymbolTree *
+ide_ctags_symbol_tree_new (GPtrArray *ar)
+{
+  IdeCtagsSymbolTree *self;
+
+  self = g_object_new (IDE_TYPE_CTAGS_SYMBOL_TREE, NULL);
+  self->ar = ar;
+
+  return self;
+}
+
+static void
+ide_ctags_symbol_tree_finalize (GObject *object)
+{
+  IdeCtagsSymbolTree *self = (IdeCtagsSymbolTree *)object;
+
+  g_clear_pointer (&self->ar, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (ide_ctags_symbol_tree_parent_class)->finalize (object);
+}
+
+static void
+ide_ctags_symbol_tree_class_init (IdeCtagsSymbolTreeClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = ide_ctags_symbol_tree_finalize;
+}
+
+static void
+ide_ctags_symbol_tree_init (IdeCtagsSymbolTree *self)
+{
+}
diff --git a/plugins/ctags/ide-ctags-symbol-tree.h b/plugins/ctags/ide-ctags-symbol-tree.h
new file mode 100644
index 0000000..03fc265
--- /dev/null
+++ b/plugins/ctags/ide-ctags-symbol-tree.h
@@ -0,0 +1,34 @@
+/* ide-ctags-symbol-tree.h
+ *
+ * Copyright (C) 2016 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/>.
+ */
+
+#ifndef IDE_CTAGS_SYMBOL_TREE_H
+#define IDE_CTAGS_SYMBOL_TREE_H
+
+#include <ide.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_CTAGS_SYMBOL_TREE (ide_ctags_symbol_tree_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeCtagsSymbolTree, ide_ctags_symbol_tree, IDE, CTAGS_SYMBOL_TREE, GObject)
+
+IdeCtagsSymbolTree *ide_ctags_symbol_tree_new (GPtrArray *items);
+
+G_END_DECLS
+
+#endif /* IDE_CTAGS_SYMBOL_TREE_H */


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