[gnome-builder/wip/chergert/gjs-in-c: 1129/1129] Basic port of GJS symbol resolver




commit 1677b43c6ef12a1ecede1e80eb5844ff516bf8ee
Author: Christian Hergert <chergert redhat com>
Date:   Mon May 23 20:42:49 2022 -0700

    Basic port of GJS symbol resolver
    
    We probably just have to delete all this code anyway, because it seems
    like we don't get real information from Reflect.parse() anymore. We just
    get a big fat Literal.
    
    That might mean that Reflect.parse() can't handle newer GJS stuff, or
    it might mean that JSON.stringify() needs help.

 src/plugins/gjs-symbols/gbp-gjs-code-indexer.c     |  50 ++
 src/plugins/gjs-symbols/gbp-gjs-code-indexer.h     |  31 ++
 src/plugins/gjs-symbols/gbp-gjs-symbol-node.c      | 577 +++++++++++++++++++++
 src/plugins/gjs-symbols/gbp-gjs-symbol-node.h      |  38 ++
 src/plugins/gjs-symbols/gbp-gjs-symbol-resolver.c  | 209 ++++++++
 src/plugins/gjs-symbols/gbp-gjs-symbol-resolver.h  |  31 ++
 src/plugins/gjs-symbols/gbp-gjs-symbol-tree.c      | 110 ++++
 src/plugins/gjs-symbols/gbp-gjs-symbol-tree.h      |  33 ++
 src/plugins/gjs-symbols/gjs-symbols-plugin.c       |  41 ++
 src/plugins/gjs-symbols/gjs-symbols.gresource.xml  |   7 +
 .../{gjs_symbols.plugin => gjs-symbols.plugin}     |   7 +-
 src/plugins/gjs-symbols/gjs_symbols.py             | 128 ++---
 src/plugins/gjs-symbols/meson.build                |  22 +-
 src/plugins/gjs-symbols/parse.js                   |  14 +
 14 files changed, 1210 insertions(+), 88 deletions(-)
---
diff --git a/src/plugins/gjs-symbols/gbp-gjs-code-indexer.c b/src/plugins/gjs-symbols/gbp-gjs-code-indexer.c
new file mode 100644
index 000000000..eeb491875
--- /dev/null
+++ b/src/plugins/gjs-symbols/gbp-gjs-code-indexer.c
@@ -0,0 +1,50 @@
+/* gbp-gjs-code-indexer.c
+ *
+ * Copyright 2022 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-gjs-code-indexer"
+
+#include "config.h"
+
+#include <libide-code.h>
+
+#include "gbp-gjs-code-indexer.h"
+
+struct _GbpGjsCodeIndexer
+{
+  IdeObject parent_instance;
+};
+
+static void
+code_indexer_iface_init (IdeCodeIndexerInterface *iface)
+{
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpGjsCodeIndexer, gbp_gjs_code_indexer, IDE_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (IDE_TYPE_CODE_INDEXER, code_indexer_iface_init))
+
+static void
+gbp_gjs_code_indexer_class_init (GbpGjsCodeIndexerClass *klass)
+{
+}
+
+static void
+gbp_gjs_code_indexer_init (GbpGjsCodeIndexer *self)
+{
+}
diff --git a/src/plugins/gjs-symbols/gbp-gjs-code-indexer.h b/src/plugins/gjs-symbols/gbp-gjs-code-indexer.h
new file mode 100644
index 000000000..f8a4356fc
--- /dev/null
+++ b/src/plugins/gjs-symbols/gbp-gjs-code-indexer.h
@@ -0,0 +1,31 @@
+/* gbp-gjs-code-indexer.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GJS_CODE_INDEXER (gbp_gjs_code_indexer_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGjsCodeIndexer, gbp_gjs_code_indexer, GBP, GJS_CODE_INDEXER, IdeObject)
+
+G_END_DECLS
diff --git a/src/plugins/gjs-symbols/gbp-gjs-symbol-node.c b/src/plugins/gjs-symbols/gbp-gjs-symbol-node.c
new file mode 100644
index 000000000..d8bf30301
--- /dev/null
+++ b/src/plugins/gjs-symbols/gbp-gjs-symbol-node.c
@@ -0,0 +1,577 @@
+/* gbp-gjs-symbol-node.c
+ *
+ * Copyright 2022 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-gjs-symbol-node"
+
+#include "config.h"
+
+#include "gbp-gjs-symbol-node.h"
+
+#define GET_STRING(n)     (n ? json_node_get_string(n) : NULL)
+#define GET_NODE(o,...)   (get_node_at_path (o, __VA_ARGS__, NULL))
+#define GET_OBJECT(o,...) (get_object(get_node_at_path (o, __VA_ARGS__, NULL)))
+
+struct _GbpGjsSymbolNode
+{
+  IdeSymbolNode parent_instance;
+
+  GPtrArray *children;
+
+  guint line;
+  guint line_offset;
+};
+
+static void gbp_gjs_symbol_node_load_children (GbpGjsSymbolNode *self,
+                                               JsonNode         *children);
+
+G_DEFINE_FINAL_TYPE (GbpGjsSymbolNode, gbp_gjs_symbol_node, IDE_TYPE_SYMBOL_NODE)
+
+static inline JsonObject *
+get_object (JsonNode *node)
+{
+  if (node == NULL)
+    return NULL;
+  if (JSON_NODE_HOLDS_OBJECT (node))
+    return json_node_get_object (node);
+  return NULL;
+}
+
+static inline void
+gbp_gjs_symbol_node_set_kind (GbpGjsSymbolNode *self,
+                              IdeSymbolKind     kind)
+{
+  g_object_set (self, "kind", kind, NULL);
+}
+
+static inline void
+gbp_gjs_symbol_node_set_name (GbpGjsSymbolNode *self,
+                              const char       *name)
+{
+  g_object_set (self, "name", name, NULL);
+}
+
+static void
+gbp_gjs_symbol_node_take_child (GbpGjsSymbolNode *self,
+                                GbpGjsSymbolNode *child)
+{
+  g_assert (GBP_IS_GJS_SYMBOL_NODE (self));
+  g_assert (GBP_IS_GJS_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);
+}
+
+static void
+gbp_gjs_symbol_node_dispose (GObject *object)
+{
+  GbpGjsSymbolNode *self = (GbpGjsSymbolNode *)object;
+
+  g_clear_pointer (&self->children, g_ptr_array_unref);
+
+  G_OBJECT_CLASS (gbp_gjs_symbol_node_parent_class)->dispose (object);
+}
+
+static void
+gbp_gjs_symbol_node_class_init (GbpGjsSymbolNodeClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = gbp_gjs_symbol_node_dispose;
+}
+
+static void
+gbp_gjs_symbol_node_init (GbpGjsSymbolNode *self)
+{
+}
+
+G_GNUC_NULL_TERMINATED
+static JsonNode *
+get_node_at_path (JsonObject *object,
+                  const char *first_child,
+                  ...)
+{
+  const char *child = first_child;
+  JsonNode *ret = NULL;
+  va_list args;
+
+  va_start (args, first_child);
+  while (child != NULL)
+    {
+      JsonNode *node = json_object_get_member (object, child);
+      child = va_arg (args, const char *);
+
+      if (node == NULL)
+        break;
+
+      if (child == NULL && node != NULL)
+        {
+          ret = node;
+          break;
+        }
+
+      if (!JSON_NODE_HOLDS_OBJECT (node))
+        break;
+
+      object = json_node_get_object (node);
+    }
+  va_end (args);
+
+  return ret;
+}
+
+static gboolean
+get_line_and_column (JsonObject *object,
+                     guint      *line,
+                     guint      *column)
+{
+  JsonNode *node;
+
+  g_assert (line != NULL);
+  g_assert (column != NULL);
+
+  *line = 0;
+  *column = 0;
+
+  if (object == NULL)
+    return FALSE;
+
+  if (!(node = get_node_at_path (object, "loc", "start", "line", NULL)))
+    return FALSE;
+  *line = json_node_get_int (node);
+
+  if (!(node = get_node_at_path (object, "loc", "start", "column", NULL)))
+    return FALSE;
+  *column = MAX (0, json_node_get_int (node));
+
+  return TRUE;
+}
+
+static gboolean
+is_module_import (JsonObject *object)
+{
+  if (object == NULL)
+    return FALSE;
+
+  return ide_str_equal0 ("imports", GET_STRING (GET_NODE (object, "init", "object", "name"))) ||
+         ide_str_equal0 ("imports", GET_STRING (GET_NODE (object, "init", "object", "object", "name"))) ||
+         ide_str_equal0 ("require", GET_STRING (GET_NODE (object, "init", "callee", "name")));
+}
+
+static gboolean
+is_module_exports (JsonObject *object)
+{
+  JsonObject *left;
+
+  if (object == NULL ||
+      !ide_str_equal0 ("AssignmentExpression", GET_STRING (GET_NODE (object, "expression", "type"))) ||
+      !(left = GET_OBJECT (object, "expression", "left")) ||
+      !ide_str_equal0 ("MemberExpression", GET_STRING (GET_NODE (left, "type"))) ||
+      (!ide_str_equal0 ("Identifier", GET_STRING (GET_NODE (left, "object", "type"))) ||
+       !ide_str_equal0 ("module", GET_STRING (GET_NODE (left, "object", "name")))) ||
+      (!ide_str_equal0 ("Identifier", GET_STRING (GET_NODE (left, "property", "type"))) ||
+       !ide_str_equal0 ("exports", GET_STRING (GET_NODE (left, "property", "name")))))
+    return FALSE;
+
+  return TRUE;
+}
+
+static gboolean
+is_gobject_class (JsonObject *object)
+{
+  const char *name;
+  const char *pname;
+
+  if (object == NULL)
+    return FALSE;
+
+  if (!(name = GET_STRING (GET_NODE (object, "init", "callee", "object", "name"))) ||
+      !(pname = GET_STRING (GET_NODE (object, "init", "callee", "property", "name"))))
+    return FALSE;
+
+  return strcasecmp (name, "gobject") == 0 && ide_str_equal0 (pname, "registerClass");
+}
+
+static gboolean
+is_legacy_gobject_class (JsonObject *object)
+{
+  const char *name;
+  const char *pname;
+
+  if (object == NULL)
+    return FALSE;
+
+  if (!(name = GET_STRING (GET_NODE (object, "init", "callee", "object", "name"))) ||
+      !(pname = GET_STRING (GET_NODE (object, "init", "callee", "property", "name"))))
+    return FALSE;
+
+  return (strcasecmp (name, "gobject") == 0 || strcasecmp (name, "lang") == 0) &&
+         ide_str_equal0 (pname, "Class");
+}
+
+
+
+static gboolean
+gbp_gjs_symbol_node_load_variable_decl (GbpGjsSymbolNode *parent,
+                                        JsonObject       *object)
+{
+  g_autoptr(GPtrArray) children = NULL;
+  JsonNode *decls;
+  JsonArray *decls_ar;
+  guint n_decls;
+
+  g_assert (GBP_IS_GJS_SYMBOL_NODE (parent));
+
+  if (object == NULL)
+    return TRUE;
+
+  if (!(decls = GET_NODE (object, "declarations")) ||
+      !JSON_NODE_HOLDS_ARRAY (decls) ||
+      !(decls_ar = json_node_get_array (decls)))
+    return TRUE;
+
+  children = g_ptr_array_new_with_free_func (g_object_unref);
+  n_decls = json_array_get_length (decls_ar);
+
+  for (guint i = 0; i < n_decls; i++)
+    {
+      g_autoptr(GbpGjsSymbolNode) new_child = NULL;
+      g_autoptr(GPtrArray) grandchildren = NULL;
+      JsonNode *decl = json_array_get_element (decls_ar, i);
+      JsonObject *decl_obj;
+      IdeSymbolKind kind;
+      const char *name;
+      guint line = 0;
+      guint column = 0;
+
+      if (decl == NULL ||
+          !JSON_NODE_HOLDS_OBJECT (decl) ||
+          !(decl_obj = json_node_get_object (decl)))
+        continue;
+
+      /* Destructured assignment, ignore */
+      if (ide_str_equal0 ("Identifier", GET_STRING (GET_NODE (decl_obj, "id", "type"))))
+        return TRUE;
+
+      kind = IDE_SYMBOL_KIND_VARIABLE;
+      name = GET_STRING (GET_NODE (decl_obj, "id", "name"));
+      get_line_and_column (GET_OBJECT (decl_obj, "id"), &line, &column);
+
+      if (is_module_import (decl_obj))
+        continue;
+
+      if (is_gobject_class (decl_obj))
+        {
+          JsonNode *args = GET_NODE (decl_obj, "init", "arguments");
+          JsonArray *args_ar;
+          guint n_args;
+
+          if (args == NULL ||
+              !JSON_NODE_HOLDS_ARRAY (args) ||
+              !(args_ar = json_node_get_array (args)))
+            continue;
+
+          n_args = json_array_get_length (args_ar);
+
+          for (guint j = 0; j < n_args; j++)
+            {
+              g_autoptr(GbpGjsSymbolNode) child = NULL;
+              JsonNode *arg = json_array_get_element (args_ar, j);
+              JsonNode *id;
+              JsonObject *arg_obj;
+              JsonObject *id_obj;
+
+              if (arg == NULL ||
+                  !JSON_NODE_HOLDS_OBJECT (arg) ||
+                  !(arg_obj = json_node_get_object (arg)) ||
+                  !ide_str_equal0 ("ClassExpression", GET_STRING (GET_NODE (arg_obj, "type"))) ||
+                  !(id = GET_NODE (arg_obj, "id")) ||
+                  !JSON_NODE_HOLDS_OBJECT (id) ||
+                  !(id_obj = json_node_get_object (id)))
+                continue;
+
+              get_line_and_column (id_obj, &line, &column);
+              kind = IDE_SYMBOL_KIND_CLASS;
+              name = GET_STRING (GET_NODE (id_obj, "name"));
+
+              child = g_object_new (GBP_TYPE_GJS_SYMBOL_NODE, NULL);
+              gbp_gjs_symbol_node_load_children (child, GET_NODE (arg_obj, "body"));
+              if (child->children != NULL)
+                g_ptr_array_extend_and_steal (grandchildren, g_steal_pointer (&child->children));
+
+              break;
+            }
+        }
+      else if (is_legacy_gobject_class (decl_obj))
+        {
+          g_autoptr(GbpGjsSymbolNode) child = NULL;
+          JsonNode *args;
+          JsonNode *arg;
+          JsonObject *arg_obj;
+          JsonArray *args_ar;
+
+          kind = IDE_SYMBOL_KIND_CLASS;
+
+          if (!(args = GET_NODE (decl_obj, "init", "arguments")) ||
+              !JSON_NODE_HOLDS_ARRAY (args) ||
+              !(args_ar = json_node_get_array (args)) ||
+              json_array_get_length (args_ar) < 1 ||
+              (arg = json_array_get_element (args_ar, 0)) ||
+              JSON_NODE_HOLDS_OBJECT (arg) ||
+              !(arg_obj = json_node_get_object (arg)) ||
+              !ide_str_equal0 ("ObjectExpression", GET_STRING (GET_NODE (arg_obj, "type"))))
+            continue;
+
+          kind = IDE_SYMBOL_KIND_CLASS;
+
+          child = g_object_new (GBP_TYPE_GJS_SYMBOL_NODE, NULL);
+          gbp_gjs_symbol_node_load_children (child, GET_NODE (arg_obj, "properties"));
+          if (child->children != NULL)
+            g_ptr_array_extend_and_steal (grandchildren, g_steal_pointer (&child->children));
+        }
+
+      new_child = g_object_new (GBP_TYPE_GJS_SYMBOL_NODE, NULL);
+      new_child->line = line;
+      new_child->line_offset = column;
+      new_child->children = g_steal_pointer (&grandchildren);
+      gbp_gjs_symbol_node_set_name (new_child, name);
+      gbp_gjs_symbol_node_set_kind (new_child, kind);
+      gbp_gjs_symbol_node_take_child (parent, g_steal_pointer (&new_child));
+    }
+
+  return TRUE;
+}
+
+static void
+gbp_gjs_symbol_node_load_children (GbpGjsSymbolNode *self,
+                                   JsonNode         *children)
+{
+  JsonArray *ar;
+  guint n_children;
+
+  g_assert (GBP_IS_GJS_SYMBOL_NODE (self));
+
+  if (children == NULL || !JSON_NODE_HOLDS_ARRAY (children))
+    return;
+
+  ar = json_node_get_array (children);
+  n_children = json_array_get_length (ar);
+
+  for (guint i = 0; i < n_children; i++)
+    {
+      JsonNode *node = json_array_get_element (ar, i);
+      GbpGjsSymbolNode *child;
+      JsonObject *child_obj;
+
+      if (node != NULL &&
+          JSON_NODE_HOLDS_OBJECT (node) &&
+          (child_obj = json_node_get_object (node)))
+        {
+          const char *type = GET_STRING (GET_NODE (child_obj, "type"));
+
+          if (ide_str_equal0 (type, "VariableDeclaration"))
+            gbp_gjs_symbol_node_load_variable_decl (self, child_obj);
+          else if ((child = gbp_gjs_symbol_node_new (child_obj)))
+            gbp_gjs_symbol_node_take_child (self, child);
+        }
+    }
+}
+
+static gboolean
+gbp_gjs_symbol_node_load_program (GbpGjsSymbolNode *self,
+                                  JsonObject       *object)
+{
+  g_assert (GBP_IS_GJS_SYMBOL_NODE (self));
+  g_assert (object != NULL);
+
+  gbp_gjs_symbol_node_set_kind (self, IDE_SYMBOL_KIND_PACKAGE);
+  gbp_gjs_symbol_node_set_name (self, GET_STRING (GET_NODE (object, "loc", "source")));
+  gbp_gjs_symbol_node_load_children (self, GET_NODE (object, "body"));
+
+  return TRUE;
+}
+
+static gboolean
+gbp_gjs_symbol_node_load_function_decl (GbpGjsSymbolNode *self,
+                                        JsonObject       *object)
+{
+  g_assert (GBP_IS_GJS_SYMBOL_NODE (self));
+  g_assert (object != NULL);
+
+  gbp_gjs_symbol_node_set_kind (self, IDE_SYMBOL_KIND_FUNCTION);
+  gbp_gjs_symbol_node_set_name (self, GET_STRING (GET_NODE (object, "id", "name")));
+
+  return TRUE;
+}
+
+static gboolean
+gbp_gjs_symbol_node_load_property (GbpGjsSymbolNode *self,
+                                   JsonObject       *object)
+{
+  const char *name;
+  const char *prop_kind;
+
+  g_assert (GBP_IS_GJS_SYMBOL_NODE (self));
+  g_assert (object != NULL);
+
+  if (ide_str_equal0 ("FunctionExpression", GET_NODE (object, "value", "type")))
+    return FALSE;
+
+  name = GET_STRING (GET_NODE (object, "key", "name"));
+  if (ide_str_equal0 (name, "_init"))
+    return FALSE;
+
+  prop_kind = GET_STRING (GET_NODE (object, "kind"));
+  if (prop_kind != NULL && g_strv_contains (IDE_STRV_INIT ("get", "set"), prop_kind))
+    return FALSE;
+
+  gbp_gjs_symbol_node_set_kind (self, IDE_SYMBOL_KIND_METHOD);
+  gbp_gjs_symbol_node_set_name (self, name);
+
+  return TRUE;
+}
+
+static gboolean
+gbp_gjs_symbol_node_load_class_stmt (GbpGjsSymbolNode *self,
+                                     JsonObject       *object)
+{
+  g_assert (GBP_IS_GJS_SYMBOL_NODE (self));
+  g_assert (object != NULL);
+
+  gbp_gjs_symbol_node_set_kind (self, IDE_SYMBOL_KIND_CLASS);
+  gbp_gjs_symbol_node_set_name (self, GET_STRING (GET_NODE (object, "id", "name")));
+  gbp_gjs_symbol_node_load_children (self, GET_NODE (object, "body"));
+
+  return TRUE;
+}
+
+static gboolean
+gbp_gjs_symbol_node_load_class_method (GbpGjsSymbolNode *self,
+                                       JsonObject       *object)
+{
+  const char *name;
+  const char *kind;
+
+  g_assert (GBP_IS_GJS_SYMBOL_NODE (self));
+  g_assert (object != NULL);
+
+  name = GET_STRING (GET_NODE (object, "name", "name"));
+  if (name != NULL && g_strv_contains (IDE_STRV_INIT ("constructed", "_init"), name))
+    return FALSE;
+
+  kind = GET_STRING (GET_NODE (object, "kind"));
+  if (kind != NULL && g_strv_contains (IDE_STRV_INIT ("get", "set"), kind))
+    return FALSE;
+
+  gbp_gjs_symbol_node_set_kind (self, IDE_SYMBOL_KIND_METHOD);
+  gbp_gjs_symbol_node_set_name (self, name);
+
+  return TRUE;
+}
+
+static gboolean
+gbp_gjs_symbol_node_load_expr_stmt (GbpGjsSymbolNode *self,
+                                    JsonObject       *object)
+{
+  JsonObject *klass;
+
+  g_assert (GBP_IS_GJS_SYMBOL_NODE (self));
+  g_assert (object != NULL);
+
+  if (!(klass = GET_OBJECT (object, "expression", "right")) ||
+      !ide_str_equal0 ("ClassExpression", GET_STRING (GET_NODE (klass, "type"))))
+    return FALSE;
+
+  gbp_gjs_symbol_node_set_kind (self, IDE_SYMBOL_KIND_CLASS);
+  gbp_gjs_symbol_node_set_name (self, GET_STRING (GET_NODE (klass, "id", "name")));
+  get_line_and_column (klass, &self->line, &self->line_offset);
+  gbp_gjs_symbol_node_load_children (self, GET_NODE (klass, "body"));
+
+  return TRUE;
+}
+
+static gboolean
+gbp_gjs_symbol_node_load (GbpGjsSymbolNode *self,
+                          JsonObject       *object)
+{
+  const char *type;
+
+  g_assert (GBP_IS_GJS_SYMBOL_NODE (self));
+  g_assert (object != NULL);
+
+  if (!(type = json_object_get_string_member (object, "type")))
+    return FALSE;
+
+  get_line_and_column (object, &self->line, &self->line_offset);
+
+  if (g_str_equal (type, "Program"))
+    return gbp_gjs_symbol_node_load_program (self, object);
+  else if (g_str_equal (type, "FunctionDeclaration"))
+    return gbp_gjs_symbol_node_load_function_decl (self, object);
+  else if (g_str_equal (type, "Property"))
+    return gbp_gjs_symbol_node_load_property (self, object);
+  else if (g_str_equal (type, "ClassStatement"))
+    return gbp_gjs_symbol_node_load_class_stmt (self, object);
+  else if (g_str_equal (type, "ClassMethod"))
+    return gbp_gjs_symbol_node_load_class_method (self, object);
+  else if (g_str_equal (type, "ExpressionStatement") && is_module_exports (object))
+    return gbp_gjs_symbol_node_load_expr_stmt (self, object);
+  else
+    return FALSE;
+}
+
+GbpGjsSymbolNode *
+gbp_gjs_symbol_node_new (JsonObject *object)
+{
+  g_autoptr(GbpGjsSymbolNode) self = NULL;
+
+  g_return_val_if_fail (object != NULL, NULL);
+
+  self = g_object_new (GBP_TYPE_GJS_SYMBOL_NODE, NULL);
+
+  if (!gbp_gjs_symbol_node_load (self, object))
+    return NULL;
+
+  return g_steal_pointer (&self);
+}
+
+guint
+gbp_gjs_symbol_node_get_n_children (GbpGjsSymbolNode *self)
+{
+  g_return_val_if_fail (GBP_IS_GJS_SYMBOL_NODE (self), 0);
+
+  if (self->children == NULL)
+    return 0;
+
+  return self->children->len;
+}
+
+IdeSymbolNode *
+gbp_gjs_symbol_node_get_nth_child (GbpGjsSymbolNode *self,
+                                   guint             nth_child)
+{
+  g_return_val_if_fail (GBP_IS_GJS_SYMBOL_NODE (self), NULL);
+
+  if (self->children == NULL || nth_child >= self->children->len)
+    return NULL;
+
+  return g_object_ref (g_ptr_array_index (self->children, nth_child));
+}
diff --git a/src/plugins/gjs-symbols/gbp-gjs-symbol-node.h b/src/plugins/gjs-symbols/gbp-gjs-symbol-node.h
new file mode 100644
index 000000000..36731e488
--- /dev/null
+++ b/src/plugins/gjs-symbols/gbp-gjs-symbol-node.h
@@ -0,0 +1,38 @@
+/* gbp-gjs-symbol-node.h
+ *
+ * Copyright 2022 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 <json-glib/json-glib.h>
+
+#include <libide-code.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GJS_SYMBOL_NODE (gbp_gjs_symbol_node_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGjsSymbolNode, gbp_gjs_symbol_node, GBP, GJS_SYMBOL_NODE, IdeSymbolNode)
+
+GbpGjsSymbolNode *gbp_gjs_symbol_node_new            (JsonObject       *object);
+guint             gbp_gjs_symbol_node_get_n_children (GbpGjsSymbolNode *self);
+IdeSymbolNode    *gbp_gjs_symbol_node_get_nth_child  (GbpGjsSymbolNode *self,
+                                                      guint             nth_child);
+
+G_END_DECLS
diff --git a/src/plugins/gjs-symbols/gbp-gjs-symbol-resolver.c 
b/src/plugins/gjs-symbols/gbp-gjs-symbol-resolver.c
new file mode 100644
index 000000000..95ba6fd6a
--- /dev/null
+++ b/src/plugins/gjs-symbols/gbp-gjs-symbol-resolver.c
@@ -0,0 +1,209 @@
+/* gbp-gjs-symbol-resolver.c
+ *
+ * Copyright 2022 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-gjs-symbol-resolver"
+
+#include "config.h"
+
+#include <errno.h>
+#include <json-glib/json-glib.h>
+#include <unistd.h>
+
+#include <libide-code.h>
+#include <libide-foundry.h>
+#include <libide-threading.h>
+
+#include "gbp-gjs-symbol-resolver.h"
+#include "gbp-gjs-symbol-tree.h"
+
+struct _GbpGjsSymbolResolver
+{
+  IdeObject parent_instance;
+};
+
+static void
+gbp_gjs_symbol_resolver_get_symbol_tree_cb (GObject      *object,
+                                            GAsyncResult *result,
+                                            gpointer      user_data)
+{
+  IdeSubprocess *subprocess = (IdeSubprocess *)object;
+  g_autoptr(GbpGjsSymbolNode) node = NULL;
+  g_autoptr(JsonParser) parser = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autofree char *stdout_buf = NULL;
+  JsonObject *obj;
+  JsonNode *root;
+
+  IDE_ENTRY;
+
+  g_assert (IDE_IS_SUBPROCESS (subprocess));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!ide_subprocess_communicate_utf8_finish (subprocess, result, &stdout_buf, NULL, &error))
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  g_print ("GJS: %s\n", stdout_buf);
+
+  parser = json_parser_new ();
+
+  if (!json_parser_load_from_data (parser, stdout_buf, -1, &error))
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  if (!(root = json_parser_get_root (parser)) ||
+      !JSON_NODE_HOLDS_OBJECT (root) ||
+      !(obj = json_node_get_object (root)) ||
+      !(node = gbp_gjs_symbol_node_new (obj)))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_INVALID_DATA,
+                                 "Reflect.parse() returned invalid data");
+      IDE_EXIT;
+    }
+
+  ide_task_return_object (task, gbp_gjs_symbol_tree_new (node));
+
+  IDE_EXIT;
+}
+
+static void
+gbp_gjs_symbol_resolver_get_symbol_tree_async (IdeSymbolResolver   *resolver,
+                                               GFile               *file,
+                                               GBytes              *contents,
+                                               GCancellable        *cancellable,
+                                               GAsyncReadyCallback  callback,
+                                               gpointer             user_data)
+{
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
+  g_autoptr(IdeContext) context = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GBytes) script = NULL;
+  g_autoptr(IdeTask) task = NULL;
+  g_autofree char *name = NULL;
+  int fd;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_GJS_SYMBOL_RESOLVER (resolver));
+  g_assert (G_IS_FILE (file));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (resolver, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_gjs_symbol_resolver_get_symbol_tree_async);
+
+  script = g_resources_lookup_data ("/plugins/gjs-symbols/parse.js", 0, NULL);
+  name = g_file_get_basename (file);
+
+  g_assert (script != NULL);
+  g_assert (name != NULL);
+
+  if (!(context = ide_object_ref_context (IDE_OBJECT (resolver))) ||
+      !(launcher = ide_foundry_get_launcher_for_context (context, "gjs", "/usr/bin/gjs", NULL)))
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_NOT_SUPPORTED,
+                                 "A working `gjs` could not be found");
+      IDE_EXIT;
+    }
+
+  if (contents != NULL)
+    fd = ide_foundry_bytes_to_memfd (contents, "gjs-symbols-data");
+  else
+    fd = ide_foundry_file_to_memfd (file, "gjs-symbols-data");
+
+  if (fd < 0)
+    {
+      ide_task_return_new_error (task,
+                                 G_IO_ERROR,
+                                 G_IO_ERROR_FAILED,
+                                 "Failed to open temporary file: %s",
+                                 g_strerror (errno));
+      IDE_EXIT;
+    }
+
+  ide_subprocess_launcher_set_flags (launcher,
+                                     (G_SUBPROCESS_FLAGS_STDOUT_PIPE |
+                                      G_SUBPROCESS_FLAGS_STDERR_SILENCE));
+  ide_subprocess_launcher_take_fd (launcher, fd, 3);
+  ide_subprocess_launcher_push_argv (launcher, "-c");
+  ide_subprocess_launcher_push_argv (launcher, (const char *)g_bytes_get_data (script, NULL));
+  ide_subprocess_launcher_push_argv (launcher, name);
+
+  if (!(subprocess = ide_subprocess_launcher_spawn (launcher, NULL, &error)))
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      IDE_EXIT;
+    }
+
+  ide_subprocess_communicate_utf8_async (subprocess,
+                                         NULL,
+                                         cancellable,
+                                         gbp_gjs_symbol_resolver_get_symbol_tree_cb,
+                                         g_steal_pointer (&task));
+
+  IDE_EXIT;
+}
+
+static IdeSymbolTree *
+gbp_gjs_symbol_resolver_get_symbol_tree_finish (IdeSymbolResolver  *resolver,
+                                                GAsyncResult       *result,
+                                                GError            **error)
+{
+  IdeSymbolTree *ret;
+
+  IDE_ENTRY;
+
+  g_assert (GBP_IS_GJS_SYMBOL_RESOLVER (resolver));
+  g_assert (IDE_IS_TASK (result));
+
+  ret = ide_task_propagate_object (IDE_TASK (result), error);
+
+  IDE_RETURN (ret);
+}
+
+static void
+symbol_resolver_iface_init (IdeSymbolResolverInterface *iface)
+{
+  iface->get_symbol_tree_async = gbp_gjs_symbol_resolver_get_symbol_tree_async;
+  iface->get_symbol_tree_finish = gbp_gjs_symbol_resolver_get_symbol_tree_finish;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpGjsSymbolResolver, gbp_gjs_symbol_resolver, IDE_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (IDE_TYPE_SYMBOL_RESOLVER, symbol_resolver_iface_init))
+
+static void
+gbp_gjs_symbol_resolver_class_init (GbpGjsSymbolResolverClass *klass)
+{
+}
+
+static void
+gbp_gjs_symbol_resolver_init (GbpGjsSymbolResolver *self)
+{
+}
diff --git a/src/plugins/gjs-symbols/gbp-gjs-symbol-resolver.h 
b/src/plugins/gjs-symbols/gbp-gjs-symbol-resolver.h
new file mode 100644
index 000000000..4e7f8e618
--- /dev/null
+++ b/src/plugins/gjs-symbols/gbp-gjs-symbol-resolver.h
@@ -0,0 +1,31 @@
+/* gbp-gjs-symbol-resolver.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-core.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GJS_SYMBOL_RESOLVER (gbp_gjs_symbol_resolver_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGjsSymbolResolver, gbp_gjs_symbol_resolver, GBP, GJS_SYMBOL_RESOLVER, IdeObject)
+
+G_END_DECLS
diff --git a/src/plugins/gjs-symbols/gbp-gjs-symbol-tree.c b/src/plugins/gjs-symbols/gbp-gjs-symbol-tree.c
new file mode 100644
index 000000000..4b36d9738
--- /dev/null
+++ b/src/plugins/gjs-symbols/gbp-gjs-symbol-tree.c
@@ -0,0 +1,110 @@
+/* gbp-gjs-symbol-tree.c
+ *
+ * Copyright 2022 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-gjs-symbol-tree"
+
+#include "config.h"
+
+#include <libide-code.h>
+
+#include "gbp-gjs-symbol-tree.h"
+
+struct _GbpGjsSymbolTree
+{
+  GObject           parent_instance;
+  GbpGjsSymbolNode *root;
+};
+
+static guint
+gbp_gjs_symbol_tree_get_n_children (IdeSymbolTree *tree,
+                                    IdeSymbolNode *node)
+{
+  GbpGjsSymbolTree *self = (GbpGjsSymbolTree *)tree;
+
+  g_assert (GBP_IS_GJS_SYMBOL_TREE (self));
+  g_assert (!node || GBP_IS_GJS_SYMBOL_NODE (node));
+
+  if (node == NULL)
+    return gbp_gjs_symbol_node_get_n_children (self->root);
+  else
+    return gbp_gjs_symbol_node_get_n_children (GBP_GJS_SYMBOL_NODE (node));
+}
+
+static IdeSymbolNode *
+gbp_gjs_symbol_tree_get_nth_child (IdeSymbolTree *tree,
+                                   IdeSymbolNode *node,
+                                   guint          nth_child)
+{
+  GbpGjsSymbolTree *self = (GbpGjsSymbolTree *)tree;
+
+  g_assert (GBP_IS_GJS_SYMBOL_TREE (self));
+  g_assert (!node || GBP_IS_GJS_SYMBOL_NODE (node));
+
+  if (node == NULL)
+    return gbp_gjs_symbol_node_get_nth_child (self->root, nth_child);
+  else
+    return gbp_gjs_symbol_node_get_nth_child (GBP_GJS_SYMBOL_NODE (node), nth_child);
+}
+
+static void
+symbol_tree_iface_init (IdeSymbolTreeInterface *iface)
+{
+  iface->get_n_children = gbp_gjs_symbol_tree_get_n_children;
+  iface->get_nth_child = gbp_gjs_symbol_tree_get_nth_child;
+}
+
+G_DEFINE_FINAL_TYPE_WITH_CODE (GbpGjsSymbolTree, gbp_gjs_symbol_tree, G_TYPE_OBJECT,
+                               G_IMPLEMENT_INTERFACE (IDE_TYPE_SYMBOL_TREE, symbol_tree_iface_init))
+
+static void
+gbp_gjs_symbol_tree_finalize (GObject *object)
+{
+  GbpGjsSymbolTree *self = (GbpGjsSymbolTree *)object;
+
+  g_clear_object (&self->root);
+
+  G_OBJECT_CLASS (gbp_gjs_symbol_tree_parent_class)->finalize (object);
+}
+
+static void
+gbp_gjs_symbol_tree_class_init (GbpGjsSymbolTreeClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gbp_gjs_symbol_tree_finalize;
+}
+
+static void
+gbp_gjs_symbol_tree_init (GbpGjsSymbolTree *self)
+{
+}
+
+GbpGjsSymbolTree *
+gbp_gjs_symbol_tree_new (GbpGjsSymbolNode *root)
+{
+  GbpGjsSymbolTree *self;
+
+  g_return_val_if_fail (GBP_IS_GJS_SYMBOL_NODE (root), NULL);
+
+  self = g_object_new (GBP_TYPE_GJS_SYMBOL_TREE, NULL);
+  self->root = g_object_ref (root);
+
+  return self;
+}
diff --git a/src/plugins/gjs-symbols/gbp-gjs-symbol-tree.h b/src/plugins/gjs-symbols/gbp-gjs-symbol-tree.h
new file mode 100644
index 000000000..b6fb38544
--- /dev/null
+++ b/src/plugins/gjs-symbols/gbp-gjs-symbol-tree.h
@@ -0,0 +1,33 @@
+/* gbp-gjs-symbol-tree.h
+ *
+ * Copyright 2022 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 "gbp-gjs-symbol-node.h"
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_GJS_SYMBOL_TREE (gbp_gjs_symbol_tree_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpGjsSymbolTree, gbp_gjs_symbol_tree, GBP, GJS_SYMBOL_TREE, GObject)
+
+GbpGjsSymbolTree *gbp_gjs_symbol_tree_new (GbpGjsSymbolNode *root);
+
+G_END_DECLS
diff --git a/src/plugins/gjs-symbols/gjs-symbols-plugin.c b/src/plugins/gjs-symbols/gjs-symbols-plugin.c
new file mode 100644
index 000000000..b9d6d5be0
--- /dev/null
+++ b/src/plugins/gjs-symbols/gjs-symbols-plugin.c
@@ -0,0 +1,41 @@
+/* gjs-symbols-plugin.c
+ *
+ * Copyright 2022 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 "gjs-symbols-plugin"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+
+#include <libide-code.h>
+
+#include "gbp-gjs-code-indexer.h"
+#include "gbp-gjs-symbol-resolver.h"
+
+_IDE_EXTERN void
+_gbp_gjs_symbols_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_SYMBOL_RESOLVER,
+                                              GBP_TYPE_GJS_SYMBOL_RESOLVER);
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_CODE_INDEXER,
+                                              GBP_TYPE_GJS_CODE_INDEXER);
+}
diff --git a/src/plugins/gjs-symbols/gjs-symbols.gresource.xml 
b/src/plugins/gjs-symbols/gjs-symbols.gresource.xml
new file mode 100644
index 000000000..3d773032c
--- /dev/null
+++ b/src/plugins/gjs-symbols/gjs-symbols.gresource.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/plugins/gjs-symbols">
+    <file>gjs-symbols.plugin</file>
+    <file>parse.js</file>
+  </gresource>
+</gresources>
diff --git a/src/plugins/gjs-symbols/gjs_symbols.plugin b/src/plugins/gjs-symbols/gjs-symbols.plugin
similarity index 67%
rename from src/plugins/gjs-symbols/gjs_symbols.plugin
rename to src/plugins/gjs-symbols/gjs-symbols.plugin
index c715af17c..fb3979eea 100644
--- a/src/plugins/gjs-symbols/gjs_symbols.plugin
+++ b/src/plugins/gjs-symbols/gjs-symbols.plugin
@@ -1,13 +1,12 @@
 [Plugin]
 Authors=Patrick Griffis <tingping tingping se>
 Builtin=true
-Copyright=Copyright © 2017 Patrick Griffis
+Copyright=Copyright © 2017 Patrick Griffis, Copyright © 2022 Christian Hergert
 Description=Provides a symbol resolver for JavaScript using GJS.
-Loader=python3
-Module=gjs_symbols
+Embedded=_gbp_gjs_symbols_register_types
+Module=gjs-symbols
 Name=GJS Symbol Resolver
 X-Code-Indexer-Languages-Priority=0
 X-Code-Indexer-Languages=js
 X-Symbol-Resolver-Languages-Priority=0
 X-Symbol-Resolver-Languages=js
-X-Builder-ABI=@PACKAGE_ABI@
diff --git a/src/plugins/gjs-symbols/gjs_symbols.py b/src/plugins/gjs-symbols/gjs_symbols.py
index c98eb23c2..768a924e5 100644
--- a/src/plugins/gjs-symbols/gjs_symbols.py
+++ b/src/plugins/gjs-symbols/gjs_symbols.py
@@ -42,7 +42,7 @@ class JsSymbolNode(Ide.SymbolNode):
         self.children = children
 
     def do_get_location_async(self, cancellable, callback, user_data=None):
-        task = Gio.Task.new(self, cancellable, callback)
+        task = Ide.Task.new(self, cancellable, callback)
         task.return_boolean(True)
 
     def do_get_location_finish(self, result):
@@ -248,97 +248,73 @@ try {
         data = ARGV[0];
     }
     print(JSON.stringify(Reflect.parse(data, {source: '%s'})));
+
+    subprocess =
 } catch (e) {
     imports.system.exit(1);
 }
 """.replace('\n', ' ')
 
-class GjsSymbolProvider(Ide.Object, Ide.SymbolResolver):
-    def __init__(self):
-        super().__init__()
+def _get_launcher(context, file_):
+    file_path = file_.get_path()
+    script = JS_SCRIPT % file_path.replace('\\', '\\\\').replace("'", "\\'")
+    unsaved_file = Ide.UnsavedFiles.from_context(context).get_unsaved_file(file_)
+    srcdir = context.ref_workdir().get_path()
+    launcher = None
 
-    @staticmethod
-    def _get_launcher(context, file_):
-        file_path = file_.get_path()
-        script = JS_SCRIPT % file_path.replace('\\', '\\\\').replace("'", "\\'")
-        unsaved_file = Ide.UnsavedFiles.from_context(context).get_unsaved_file(file_)
-        srcdir = context.ref_workdir().get_path()
-        launcher = None
-
-        if context.has_project():
-            pipeline = Ide.BuildManager.from_context(context).get_pipeline()
-            if pipeline is not None and pipeline.contains_program_in_path('gjs'):
-                launcher = pipeline.create_launcher()
-                srcdir = pipeline.get_srcdir()
-
-        if launcher is None:
-            launcher = Ide.SubprocessLauncher.new(0)
-
-        launcher.set_cwd(srcdir)
-        launcher.set_flags(Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_SILENCE)
-        launcher.push_args(('gjs', '-c', script))
-
-        if unsaved_file is not None:
-            launcher.push_argv(unsaved_file.get_content().get_data().decode('utf-8'))
-        else:
-            launcher.push_args(('--file', file_path))
+    if context.has_project():
+        pipeline = Ide.BuildManager.from_context(context).get_pipeline()
+        if pipeline is not None and pipeline.contains_program_in_path('gjs'):
+            launcher = pipeline.create_launcher()
+            srcdir = pipeline.get_srcdir()
 
-        return launcher
+    if launcher is None:
+        launcher = Ide.SubprocessLauncher.new(0)
 
-    def do_lookup_symbol_async(self, location, cancellable, callback, user_data=None):
-        task = Gio.Task.new(self, cancellable, callback)
-        task.return_error(GLib.Error('Not implemented'))
+    launcher.set_cwd(srcdir)
+    launcher.set_flags(Gio.SubprocessFlags.STDOUT_PIPE | Gio.SubprocessFlags.STDERR_SILENCE)
+    launcher.push_args(('gjs', '-c', script))
 
-    def do_lookup_symbol_finish(self, result):
-        result.propagate_boolean()
-        return None
+    if unsaved_file is not None:
+        launcher.push_argv(unsaved_file.get_content().get_data().decode('utf-8'))
+    else:
+        launcher.push_args(('--file', file_path))
 
-    def do_get_symbol_tree_async(self, file_, buffer_, cancellable, callback, user_data=None):
-        task = Gio.Task.new(self, cancellable, callback)
-        launcher = self._get_launcher(self.get_context(), file_)
+    return launcher
 
-        threading.Thread(target=self._get_tree_thread, args=(task, launcher, file_),
-                         name='gjs-symbols-thread').start()
+def _get_tree_thread(task, launcher, file_):
+    try:
+        proc = launcher.spawn()
+        success, stdout, stderr = proc.communicate_utf8(None, None)
 
-    def _get_tree_thread(self, task, launcher, file_):
-        try:
-            proc = launcher.spawn()
-            success, stdout, stderr = proc.communicate_utf8(None, None)
-
-            if not success:
-                task.return_error(GLib.Error('Failed to run gjs'))
-                return
-
-            task.symbol_tree = JsSymbolTree(json.loads(stdout), file_)
-        except GLib.Error as err:
-            task.return_error(err)
-        except (json.JSONDecodeError, UnicodeDecodeError) as e:
-            task.return_error(GLib.Error('Failed to decode gjs json: {}'.format(e)))
-        except (IndexError, KeyError) as e:
-            task.return_error(GLib.Error('Failed to extract information from ast: {}'.format(e)))
-        else:
-            task.return_boolean(True)
+        if not success:
+            task.return_error(GLib.Error('Failed to run gjs'))
+            return
 
-    def do_get_symbol_tree_finish(self, result):
-        if result.propagate_boolean():
-            return result.symbol_tree
+        task.symbol_tree = JsSymbolTree(json.loads(stdout), file_)
+    except GLib.Error as err:
+        task.return_error(err)
+    except (json.JSONDecodeError, UnicodeDecodeError) as e:
+        task.return_error(GLib.Error('Failed to decode gjs json: {}'.format(e)))
+    except (IndexError, KeyError) as e:
+        task.return_error(GLib.Error('Failed to extract information from ast: {}'.format(e)))
+    else:
+        task.return_boolean(True)
 
-    def do_load(self):
-        pass
 
-    def do_find_references_async(self, location, language_id, cancellable, callback, user_data=None):
-        task = Gio.Task.new(self, cancellable, callback)
-        task.return_error(GLib.Error('Not implemented'))
+class GjsSymbolProvider(Ide.Object, Ide.SymbolResolver):
 
-    def do_find_references_finish(self, result):
-        return result.propagate_boolean()
+    def do_get_symbol_tree_async(self, file_, buffer_, cancellable, callback, data):
+        task = Ide.Task.new(self, cancellable, callback)
+        launcher = _get_launcher(self.get_context(), file_)
 
-    def do_find_nearest_scope_async(self, location, cancellable, callback, user_data=None):
-        task = Gio.Task.new(self, cancellable, callback)
-        task.return_error(GLib.Error('Not implemented'))
+        threading.Thread(target=_get_tree_thread,
+                         args=(task, launcher, file_),
+                         name='gjs-symbols-thread').start()
 
-    def do_find_nearest_scope_finish(self, result):
-        return result.propagate_boolean()
+    def do_get_symbol_tree_finish(self, result):
+        if result.propagate_boolean():
+            return result.symbol_tree
 
 
 class JsCodeIndexEntries(GObject.Object, Ide.CodeIndexEntries):
@@ -430,7 +406,7 @@ class GjsCodeIndexer(Ide.Object, Ide.CodeIndexer):
             self.active = False
 
     def do_index_file_async(self, file_, build_flags, cancellable, callback, data):
-        task = Gio.Task.new(self, cancellable, callback)
+        task = Ide.Task.new(self, cancellable, callback)
         task.entries = None
         task.file = file_
 
@@ -450,7 +426,7 @@ class GjsCodeIndexer(Ide.Object, Ide.CodeIndexer):
 
     def do_generate_key_async(self, location, flags, cancellable, callback, user_data=None):
         # print('generate key')
-        task = Gio.Task.new(self, cancellable, callback)
+        task = Ide.Task.new(self, cancellable, callback)
         task.return_error(GLib.Error('Not implemented'))
 
     def do_generate_key_finish(self, result):
diff --git a/src/plugins/gjs-symbols/meson.build b/src/plugins/gjs-symbols/meson.build
index 43ccc9724..51d8dae87 100644
--- a/src/plugins/gjs-symbols/meson.build
+++ b/src/plugins/gjs-symbols/meson.build
@@ -1,13 +1,19 @@
 if get_option('plugin_gjs_symbols')
 
-install_data('gjs_symbols.py', install_dir: plugindir)
-
-configure_file(
-          input: 'gjs_symbols.plugin',
-         output: 'gjs_symbols.plugin',
-  configuration: config_h,
-        install: true,
-    install_dir: plugindir,
+plugins_sources += files([
+  'gjs-symbols-plugin.c',
+  'gbp-gjs-symbol-resolver.c',
+  'gbp-gjs-symbol-node.c',
+  'gbp-gjs-symbol-tree.c',
+  'gbp-gjs-code-indexer.c',
+])
+
+plugin_gjs_symbols_resources = gnome.compile_resources(
+  'gjs-symbols-resources',
+  'gjs-symbols.gresource.xml',
+  c_name: 'gbp_gjs_symbols',
 )
 
+plugins_sources += plugin_gjs_symbols_resources
+
 endif
diff --git a/src/plugins/gjs-symbols/parse.js b/src/plugins/gjs-symbols/parse.js
new file mode 100644
index 000000000..c8d39ce7a
--- /dev/null
+++ b/src/plugins/gjs-symbols/parse.js
@@ -0,0 +1,14 @@
+try {
+    const DATA_FD = 0;
+    const Gio = imports.gi.Gio;
+    const GLib = imports.gi.GLib;
+
+    let input = Gio.UnixInputStream.new(DATA_FD, true);
+    let reader = Gio.DataInputStream.new(input);
+    let [data, len] = reader.read_upto("", -1, null);
+
+    print(JSON.stringify(Reflect.parse(data, {source: ARGV[1], line: 0, target: "module"})));
+} catch (e) {
+    print(e);
+    imports.system.exit(1);
+}


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