[gnome-builder] clang: add minimal implementation of symbol tree for Clang



commit 22173ed20994ca16c5aebb9d3cdc7122127ee196
Author: Christian Hergert <christian hergert me>
Date:   Thu Jun 18 16:48:19 2015 -0700

    clang: add minimal implementation of symbol tree for Clang
    
    Lots still to be done here. For example, we need to support more clang
    cursor types. We also need to create source locations for the nodes so
    that we can jump to the location when activated in the tree.
    
    We should make sure we do source location lookup via a vfunc/signal so
    that it can be done lazily as well. No sense in creating all those
    file path strings until they are needed.

 libide/Makefile.am                        |    2 +
 libide/clang/ide-clang-private.h          |   18 ++-
 libide/clang/ide-clang-symbol-node.c      |  187 +++++++++++++++++++++++++++++
 libide/clang/ide-clang-symbol-node.h      |   32 +++++
 libide/clang/ide-clang-symbol-tree.c      |  167 +++++++++++++++++++++++++-
 libide/clang/ide-clang-symbol-tree.h      |    4 +
 libide/clang/ide-clang-translation-unit.c |   17 +--
 7 files changed, 409 insertions(+), 18 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index ef4166b..b658b24 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -221,6 +221,8 @@ libide_1_0_la_SOURCES = \
        clang/ide-clang-service.h \
        clang/ide-clang-symbol-resolver.c \
        clang/ide-clang-symbol-resolver.h \
+       clang/ide-clang-symbol-node.c \
+       clang/ide-clang-symbol-node.h \
        clang/ide-clang-symbol-tree.c \
        clang/ide-clang-symbol-tree.h \
        clang/ide-clang-translation-unit.c \
diff --git a/libide/clang/ide-clang-private.h b/libide/clang/ide-clang-private.h
index 0f73654..496b9f2 100644
--- a/libide/clang/ide-clang-private.h
+++ b/libide/clang/ide-clang-private.h
@@ -24,17 +24,23 @@
 #include "ide-types.h"
 
 #include "ide-clang-service.h"
+#include "ide-clang-symbol-node.h"
 #include "ide-clang-translation-unit.h"
 #include "ide-highlight-index.h"
 
 G_BEGIN_DECLS
 
-IdeClangTranslationUnit *_ide_clang_translation_unit_new (IdeContext        *context,
-                                                          CXTranslationUnit  tu,
-                                                          GFile             *file,
-                                                          IdeHighlightIndex *index,
-                                                          gint64             serial);
-void                     _ide_clang_dispose_string       (CXString          *str);
+IdeClangTranslationUnit *_ide_clang_translation_unit_new     (IdeContext        *context,
+                                                              CXTranslationUnit  tu,
+                                                              GFile             *file,
+                                                              IdeHighlightIndex *index,
+                                                              gint64             serial);
+void                     _ide_clang_dispose_string           (CXString          *str);
+IdeSymbolNode           *_ide_clang_symbol_node_new          (CXCursor           cursor);
+CXCursor                 _ide_clang_symbol_node_get_cursor   (IdeClangSymbolNode *self);
+GArray                  *_ide_clang_symbol_node_get_children (IdeClangSymbolNode *self);
+void                     _ide_clang_symbol_node_set_children (IdeClangSymbolNode *self,
+                                                              GArray             *children);
 
 G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC (CXString, _ide_clang_dispose_string)
 
diff --git a/libide/clang/ide-clang-symbol-node.c b/libide/clang/ide-clang-symbol-node.c
new file mode 100644
index 0000000..cfb25e7
--- /dev/null
+++ b/libide/clang/ide-clang-symbol-node.c
@@ -0,0 +1,187 @@
+/* ide-clang-symbol-node.c
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <clang-c/Index.h>
+
+#include "ide-clang-symbol-node.h"
+#include "ide-symbol.h"
+
+struct _IdeClangSymbolNode
+{
+  IdeSymbolNode parent_instance;
+
+  CXCursor  cursor;
+  GArray   *children;
+};
+
+G_DEFINE_TYPE (IdeClangSymbolNode, ide_clang_symbol_node, IDE_TYPE_SYMBOL_NODE)
+
+static enum CXChildVisitResult
+find_child_type (CXCursor     cursor,
+                 CXCursor     parent,
+                 CXClientData user_data)
+{
+  enum CXCursorKind *child_kind = user_data;
+  enum CXCursorKind kind = clang_getCursorKind (cursor);
+
+  switch ((int)kind)
+    {
+    case CXCursor_StructDecl:
+    case CXCursor_UnionDecl:
+    case CXCursor_EnumDecl:
+      *child_kind = kind;
+      return CXChildVisit_Break;
+
+    case CXCursor_TypeRef:
+      cursor = clang_getCursorReferenced (cursor);
+      *child_kind = clang_getCursorKind (cursor);
+      return CXChildVisit_Break;
+
+    default:
+      break;
+    }
+
+  return CXChildVisit_Continue;
+}
+
+static IdeSymbolKind
+get_symbol_kind (CXCursor        cursor,
+                 IdeSymbolFlags *flags)
+{
+  enum CXAvailabilityKind availability;
+  enum CXCursorKind cxkind;
+  IdeSymbolFlags local_flags = 0;
+  IdeSymbolKind kind = 0;
+
+  availability = clang_getCursorAvailability (cursor);
+  if (availability == CXAvailability_Deprecated)
+    local_flags |= IDE_SYMBOL_FLAGS_IS_DEPRECATED;
+
+  cxkind = clang_getCursorKind (cursor);
+
+  if (cxkind == CXCursor_TypedefDecl)
+    {
+      enum CXCursorKind child_kind = 0;
+
+      clang_visitChildren (cursor, find_child_type, &child_kind);
+      cxkind = child_kind;
+    }
+
+  switch ((int)cxkind)
+    {
+    case CXCursor_StructDecl:
+      kind = IDE_SYMBOL_STRUCT;
+      break;
+
+    case CXCursor_UnionDecl:
+      kind = IDE_SYMBOL_UNION;
+      break;
+
+    case CXCursor_ClassDecl:
+      kind = IDE_SYMBOL_CLASS;
+      break;
+
+    case CXCursor_FunctionDecl:
+      kind = IDE_SYMBOL_FUNCTION;
+      break;
+
+    case CXCursor_EnumDecl:
+      kind = IDE_SYMBOL_ENUM;
+      break;
+
+    case CXCursor_EnumConstantDecl:
+      kind = IDE_SYMBOL_ENUM_VALUE;
+      break;
+
+    case CXCursor_FieldDecl:
+      kind = IDE_SYMBOL_FIELD;
+      break;
+
+    default:
+      break;
+    }
+
+  *flags = local_flags;
+
+  return kind;
+}
+
+IdeClangSymbolNode *
+_ide_clang_symbol_node_new (CXCursor cursor)
+{
+  IdeClangSymbolNode *self;
+  IdeSymbolFlags flags = 0;
+  IdeSymbolKind kind;
+  CXString cxname;
+
+  /*
+   * TODO: Create IdeSourceLocation for cursor.
+   */
+
+  kind = get_symbol_kind (cursor, &flags);
+  cxname = clang_getCursorSpelling (cursor);
+
+  self = g_object_new (IDE_TYPE_CLANG_SYMBOL_NODE,
+                       "kind", kind,
+                       "flags", flags,
+                       "name", clang_getCString (cxname),
+                       NULL);
+
+  self->cursor = cursor;
+
+  clang_disposeString (cxname);
+
+  return self;
+}
+
+CXCursor
+_ide_clang_symbol_node_get_cursor (IdeClangSymbolNode *self)
+{
+  g_return_val_if_fail (IDE_IS_CLANG_SYMBOL_NODE (self), clang_getNullCursor ());;
+
+  return self->cursor;
+}
+
+static void
+ide_clang_symbol_node_class_init (IdeClangSymbolNodeClass *klass)
+{
+}
+
+static void
+ide_clang_symbol_node_init (IdeClangSymbolNode *self)
+{
+}
+
+GArray *
+_ide_clang_symbol_node_get_children (IdeClangSymbolNode *self)
+{
+  g_return_val_if_fail (IDE_IS_CLANG_SYMBOL_NODE (self), NULL);
+
+  return self->children;
+}
+
+void
+_ide_clang_symbol_node_set_children (IdeClangSymbolNode *self,
+                                     GArray             *children)
+{
+  g_return_if_fail (IDE_IS_CLANG_SYMBOL_NODE (self));
+  g_return_if_fail (self->children == NULL);
+  g_return_if_fail (children != NULL);
+
+  self->children = g_array_ref (children);
+}
diff --git a/libide/clang/ide-clang-symbol-node.h b/libide/clang/ide-clang-symbol-node.h
new file mode 100644
index 0000000..ed7afca
--- /dev/null
+++ b/libide/clang/ide-clang-symbol-node.h
@@ -0,0 +1,32 @@
+/* ide-clang-symbol-node.h
+ *
+ * Copyright (C) 2015 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_CLANG_SYMBOL_NODE_H
+#define IDE_CLANG_SYMBOL_NODE_H
+
+#include "ide-symbol-node.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_CLANG_SYMBOL_NODE (ide_clang_symbol_node_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeClangSymbolNode, ide_clang_symbol_node, IDE, CLANG_SYMBOL_NODE, IdeSymbolNode)
+
+G_END_DECLS
+
+#endif /* IDE_CLANG_SYMBOL_NODE_H */
diff --git a/libide/clang/ide-clang-symbol-tree.c b/libide/clang/ide-clang-symbol-tree.c
index 20007dc..3276571 100644
--- a/libide/clang/ide-clang-symbol-tree.c
+++ b/libide/clang/ide-clang-symbol-tree.c
@@ -16,8 +16,11 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <clang-c/Index.h>
 #include <glib/gi18n.h>
 
+#include "ide-clang-private.h"
+#include "ide-clang-symbol-node.h"
 #include "ide-clang-symbol-tree.h"
 #include "ide-ref-ptr.h"
 
@@ -26,8 +29,17 @@ struct _IdeClangSymbolTree
   GObject    parent_instance;
 
   IdeRefPtr *native;
+  GFile     *file;
+  gchar     *path;
+  GArray    *children;
 };
 
+typedef struct
+{
+  const gchar   *path;
+  GArray        *children;
+} TraversalState;
+
 static void symbol_tree_iface_init (IdeSymbolTreeInterface *iface);
 
 G_DEFINE_TYPE_WITH_CODE (IdeClangSymbolTree, ide_clang_symbol_tree, G_TYPE_OBJECT,
@@ -35,22 +47,139 @@ G_DEFINE_TYPE_WITH_CODE (IdeClangSymbolTree, ide_clang_symbol_tree, G_TYPE_OBJEC
 
 enum {
   PROP_0,
+  PROP_FILE,
   PROP_NATIVE,
   LAST_PROP
 };
 
 static GParamSpec *gParamSpecs [LAST_PROP];
 
+/**
+ * ide_clang_symbol_tree_get_file:
+ * @self: A #IdeClangSymbolTree.
+ *
+ * Gets the #IdeClangSymbolTree:file property.
+ *
+ * Returns: (transfer none): A #GFile.
+ */
+GFile *
+ide_clang_symbol_tree_get_file (IdeClangSymbolTree *self)
+{
+  g_return_val_if_fail (IDE_IS_CLANG_SYMBOL_TREE (self), NULL);
+
+  return self->file;
+}
+
+static void
+ide_clang_symbol_tree_set_file (IdeClangSymbolTree *self,
+                                GFile              *file)
+{
+  g_return_if_fail (IDE_IS_CLANG_SYMBOL_TREE (self));
+  g_return_if_fail (G_IS_FILE (file));
+
+  self->file = g_object_ref (file);
+  self->path = g_file_get_path (file);
+}
+
+static gboolean
+cursor_is_recognized (TraversalState *state,
+                      CXCursor        cursor)
+{
+  CXString filename;
+  CXSourceLocation cxloc;
+  CXFile file;
+  enum CXCursorKind kind;
+  gboolean ret = FALSE;
+
+  kind = clang_getCursorKind (cursor);
+
+  switch ((int)kind)
+    {
+    /*
+     * TODO: Support way more CXCursorKind.
+     */
+
+    case CXCursor_FunctionDecl:
+    case CXCursor_TypedefDecl:
+      cxloc = clang_getCursorLocation (cursor);
+      clang_getFileLocation (cxloc, &file, NULL, NULL, NULL);
+      filename = clang_getFileName (file);
+      ret = ide_str_equal0 (clang_getCString (filename), state->path);
+      clang_disposeString (filename);
+      break;
+
+    default:
+      break;
+    }
+
+  return ret;
+}
+
+static enum CXChildVisitResult
+count_recognizable_children (CXCursor     cursor,
+                             CXCursor     parent,
+                             CXClientData user_data)
+{
+  TraversalState *state = user_data;
+
+  if (cursor_is_recognized (state, cursor))
+    g_array_append_val (state->children, cursor);
+
+  return CXChildVisit_Continue;
+}
+
 static guint
 ide_clang_symbol_tree_get_n_children (IdeSymbolTree *symbol_tree,
                                       IdeSymbolNode *parent)
 {
   IdeClangSymbolTree *self = (IdeClangSymbolTree *)symbol_tree;
+  CXTranslationUnit tu;
+  CXCursor cursor;
+  TraversalState state = { 0 };
+  GArray *children = NULL;
+  guint count;
 
   g_return_val_if_fail (IDE_IS_CLANG_SYMBOL_TREE (self), 0);
-  g_return_val_if_fail (!parent || IDE_IS_SYMBOL_NODE (parent), 0);
+  g_return_val_if_fail (!parent || IDE_IS_CLANG_SYMBOL_NODE (parent), 0);
+  g_return_val_if_fail (self->native != NULL, 0);
+
+  if (parent == NULL)
+    children = self->children;
+  else
+    children = _ide_clang_symbol_node_get_children (IDE_CLANG_SYMBOL_NODE (parent));
+
+  if (children != NULL)
+    return children->len;
+
+  if (parent == NULL)
+    {
+      tu = ide_ref_ptr_get (self->native);
+      cursor = clang_getTranslationUnitCursor (tu);
+    }
+  else
+    {
+      cursor = _ide_clang_symbol_node_get_cursor (IDE_CLANG_SYMBOL_NODE (parent));
+    }
+
+  children = g_array_new (FALSE, FALSE, sizeof (CXCursor));
+
+  state.path = self->path;
+  state.children = children;
+
+  clang_visitChildren (cursor,
+                       count_recognizable_children,
+                       &state);
+
+  if (parent == NULL)
+    self->children = g_array_ref (children);
+  else
+    _ide_clang_symbol_node_set_children (IDE_CLANG_SYMBOL_NODE (parent), children);
 
-  return 0;
+  count = children->len;
+
+  g_array_unref (children);
+
+  return count;
 }
 
 static IdeSymbolNode *
@@ -59,10 +188,28 @@ ide_clang_symbol_tree_get_nth_child (IdeSymbolTree *symbol_tree,
                                      guint          nth)
 {
   IdeClangSymbolTree *self = (IdeClangSymbolTree *)symbol_tree;
+  GArray *children;
 
   g_return_val_if_fail (IDE_IS_CLANG_SYMBOL_TREE (self), NULL);
   g_return_val_if_fail (!parent || IDE_IS_SYMBOL_NODE (parent), NULL);
 
+  if (parent == NULL)
+    children = self->children;
+  else
+    children = _ide_clang_symbol_node_get_children (IDE_CLANG_SYMBOL_NODE (parent));
+
+  g_assert (children != NULL);
+
+  if (nth < children->len)
+    {
+      CXCursor cursor;
+
+      cursor = g_array_index (children, CXCursor, nth);
+      return _ide_clang_symbol_node_new (cursor);
+    }
+
+  g_warning ("nth child %u is out of bounds", nth);
+
   return NULL;
 }
 
@@ -72,6 +219,7 @@ ide_clang_symbol_tree_finalize (GObject *object)
   IdeClangSymbolTree *self = (IdeClangSymbolTree *)object;
 
   g_clear_pointer (&self->native, ide_ref_ptr_unref);
+  g_clear_pointer (&self->children, g_array_unref);
 
   G_OBJECT_CLASS (ide_clang_symbol_tree_parent_class)->finalize (object);
 }
@@ -86,6 +234,10 @@ ide_clang_symbol_tree_get_property (GObject    *object,
 
   switch (prop_id)
     {
+    case PROP_FILE:
+      g_value_set_object (value, ide_clang_symbol_tree_get_file (self));
+      break;
+
     case PROP_NATIVE:
       g_value_set_boxed (value, self->native);
       break;
@@ -105,6 +257,10 @@ ide_clang_symbol_tree_set_property (GObject      *object,
 
   switch (prop_id)
     {
+    case PROP_FILE:
+      ide_clang_symbol_tree_set_file (self, g_value_get_object (value));
+      break;
+
     case PROP_NATIVE:
       self->native = g_value_dup_boxed (value);
       break;
@@ -123,6 +279,13 @@ ide_clang_symbol_tree_class_init (IdeClangSymbolTreeClass *klass)
   object_class->get_property = ide_clang_symbol_tree_get_property;
   object_class->set_property = ide_clang_symbol_tree_set_property;
 
+  gParamSpecs [PROP_FILE] =
+    g_param_spec_object ("file",
+                         _("File"),
+                         _("File"),
+                         G_TYPE_FILE,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
   gParamSpecs [PROP_NATIVE] =
     g_param_spec_boxed ("native",
                         _("Native"),
diff --git a/libide/clang/ide-clang-symbol-tree.h b/libide/clang/ide-clang-symbol-tree.h
index 24aff08..f4376ad 100644
--- a/libide/clang/ide-clang-symbol-tree.h
+++ b/libide/clang/ide-clang-symbol-tree.h
@@ -19,6 +19,8 @@
 #ifndef IDE_CLANG_SYMBOL_TREE_H
 #define IDE_CLANG_SYMBOL_TREE_H
 
+#include <gio/gio.h>
+
 #include "ide-symbol-tree.h"
 
 G_BEGIN_DECLS
@@ -27,6 +29,8 @@ G_BEGIN_DECLS
 
 G_DECLARE_FINAL_TYPE (IdeClangSymbolTree, ide_clang_symbol_tree, IDE, CLANG_SYMBOL_TREE, GObject)
 
+GFile *ide_clang_symbol_tree_get_file (IdeClangSymbolTree *self);
+
 G_END_DECLS
 
 #endif /* IDE_CLANG_SYMBOL_TREE_H */
diff --git a/libide/clang/ide-clang-translation-unit.c b/libide/clang/ide-clang-translation-unit.c
index de759cb..e452f4a 100644
--- a/libide/clang/ide-clang-translation-unit.c
+++ b/libide/clang/ide-clang-translation-unit.c
@@ -26,6 +26,7 @@
 #include "ide-context.h"
 #include "ide-clang-completion-item.h"
 #include "ide-clang-private.h"
+#include "ide-clang-symbol-tree.h"
 #include "ide-clang-translation-unit.h"
 #include "ide-debug.h"
 #include "ide-diagnostic.h"
@@ -1078,22 +1079,18 @@ ide_clang_translation_unit_get_symbol_tree_async (IdeClangTranslationUnit *self,
                                                   gpointer                 user_data)
 {
   g_autoptr(GTask) task = NULL;
+  IdeSymbolTree *symbol_tree;
 
   g_return_if_fail (IDE_IS_CLANG_TRANSLATION_UNIT (self));
   g_return_if_fail (G_IS_FILE (file));
   g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
 
   task = g_task_new (self, cancellable, callback, user_data);
-  g_task_set_task_data (task, g_object_ref (file), g_object_unref);
-
-  /*
-   * TODO: implement IdeClangSymbolTree
-   */
-
-  g_task_return_new_error (task,
-                           G_IO_ERROR,
-                           G_IO_ERROR_NOT_SUPPORTED,
-                           "Not yet supported");
+  symbol_tree = g_object_new (IDE_TYPE_CLANG_SYMBOL_TREE,
+                              "native", self->native,
+                              "file", file,
+                              NULL);
+  g_task_return_pointer (task, symbol_tree, g_object_unref);
 }
 
 IdeSymbolTree *


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