[gnome-builder/wip/libide: 209/237] libide: add IdeGjsContext to load javascript scripts



commit 46676e3267fb761b7a20d02b399a500d838cd90e
Author: Christian Hergert <christian hergert me>
Date:   Sun Feb 15 22:30:26 2015 -0800

    libide: add IdeGjsContext to load javascript scripts
    
    You can create scripts now by placing .js files in
    ~/.config/gnome-builder/scripts/. "Context" in the script is the loaded
    IdeContext.
    
    For example:
    
      let Project = Context.get_project();
      let Vcs = Context.get_vcs();
      let BuildSystem = Context.get_build_system();
    
      print("Project Name: " + Project.get_name());
      print("Vcs: " + Vcs);
      print("Build System: " + BuildSystem);

 configure.ac                  |   15 +++-
 libide/gjs/ide-gjs-script.cpp |  224 +++++++++++++++++++++--------------------
 libide/ide.c                  |    6 +
 3 files changed, 134 insertions(+), 111 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 258f345..208c362 100644
--- a/configure.ac
+++ b/configure.ac
@@ -25,8 +25,6 @@ AC_DEFINE_UNQUOTED([GETTEXT_PACKAGE], ["$GETTEXT_PACKAGE"],
                    [GETTEXT package name])
 AM_GLIB_GNU_GETTEXT
 
-AC_PROG_CXX
-
 # Initlize libtool and things that go with it.
 m4_include([build/autotools/autoconf.d/pre-lt.m4])
 m4_include([build/autotools/autoconf.d/setup_libtool.m4])
@@ -40,6 +38,19 @@ AM_INIT_AUTOMAKE([foreign subdir-objects tar-ustar no-dist-gzip dist-xz])
 AM_MAINTAINER_MODE([enable])
 m4_include([build/autotools/autoconf.d/post-am.m4])
 
+AC_PROG_CC
+AM_PROG_CC_C_O
+AC_PROG_CXX
+AC_ISC_POSIX
+AC_HEADER_STDC
+
+AM_PROG_LIBTOOL
+
+AC_C_CONST
+
+GNOME_CXX_WARNINGS([maximum])
+GNOME_MAINTAINER_MODE_DEFINES
+
 # If we are using gtk-doc, enable it.
 # We would put this in a file but gtkdocize requires it.
 m4_ifdef([GTK_DOC_CHECK],[
diff --git a/libide/gjs/ide-gjs-script.cpp b/libide/gjs/ide-gjs-script.cpp
index 03848fc..e5f18f6 100644
--- a/libide/gjs/ide-gjs-script.cpp
+++ b/libide/gjs/ide-gjs-script.cpp
@@ -25,102 +25,69 @@
 
 #include <jsapi.h>
 
+#include "ide-context.h"
 #include "ide-gjs-script.h"
 
 struct _IdeGjsScript
 {
-  IdeScript parent_instance;
-
-  GFile      *file;
+  IdeScript   parent_instance;
   GjsContext *gjs;
 };
 
-G_DEFINE_TYPE (IdeGjsScript, ide_gjs_script, IDE_TYPE_SCRIPT)
-
-enum {
-  PROP_0,
-  PROP_FILE,
-  LAST_PROP
-};
-
-static GParamSpec *gParamSpecs [LAST_PROP];
-
-/**
- * ide_gjs_script_get_file:
- *
- * Retrieves the #GFile containing the script to be loaded in the context.
- *
- * Returns: (transfer none): A #GFile.
- */
-GFile *
-ide_gjs_script_get_file (IdeGjsScript *self)
-{
-  g_return_val_if_fail (IDE_IS_GJS_SCRIPT (self), NULL);
-
-  return self->file;
-}
-
-static void
-ide_gjs_script_set_file (IdeGjsScript *self,
-                         GFile        *file)
-{
-  g_return_if_fail (IDE_IS_GJS_SCRIPT (self));
-  g_return_if_fail (G_IS_FILE (file));
-  g_return_if_fail (!self->file);
-
-  self->file = (GFile *)g_object_ref (file);
-}
-
-static void
-set_global (GjsContext  *context,
-            const gchar *name,
-            GObject     *value)
-{
-  JSContext *jscontext;
-  JSObject *jsglobal;
-  jsval jsvalue;
+static void async_initable_iface_init (GAsyncInitableIface *iface);
 
-  jscontext = (JSContext *)gjs_context_get_native_context (context);
-  jsglobal = (JSObject *)gjs_get_global_object (jscontext);
-  jsvalue.setObject (*gjs_object_from_g_object (jscontext, value));
+G_DEFINE_TYPE_EXTENDED (IdeGjsScript, ide_gjs_script, IDE_TYPE_SCRIPT, 0,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE,
+                                               async_initable_iface_init))
 
-  if (!JS_SetProperty (jscontext, jsglobal, "Context", &jsvalue))
-    {
-      g_warning (_("Failed to set IdeContext in JavaScript runtime."));
-      return;
-    }
-}
+static const char *init_js_code = "imports.gi.Ide;\n";
 
 static void
 ide_gjs_script_load (IdeScript *script)
 {
   IdeGjsScript *self = (IdeGjsScript *)script;
   IdeContext *context;
+  GjsContext *old_current;
   g_autoptr(GError) error = NULL;
   g_autoptr(gchar) contents = NULL;
   g_autoptr(gchar) path;
+  g_autoptr(GFile) parent;
+  gchar **search_path;
+  GFile *file;
   gsize len;
   int exit_status = 0;
 
   g_return_if_fail (IDE_IS_GJS_SCRIPT (self));
   g_return_if_fail (!self->gjs);
-  g_return_if_fail (G_IS_FILE (self->file));
 
-  if (!self->file)
+  file = ide_script_get_file (IDE_SCRIPT (script));
+
+  if (!file)
     {
       g_warning (_("Attempt to load a GJS script with no filename."));
       return;
     }
 
-  path = g_file_get_basename (self->file);
+  path = g_file_get_basename (file);
 
-  if (!g_file_load_contents (self->file, NULL, &contents, &len, NULL, &error))
+  if (!g_file_load_contents (file, NULL, &contents, &len, NULL, &error))
     {
       g_warning ("%s", error->message);
       return;
     }
 
-  self->gjs = gjs_context_new ();
+  old_current = gjs_context_get_current ();
+  if (old_current)
+    gjs_context_make_current (NULL);
+
+  parent = g_file_get_parent (file);
+  search_path = g_new0 (gchar*, 2);
+  search_path[0] = g_file_get_path (parent);
+  search_path[1] = NULL;
+  self->gjs = (GjsContext *)g_object_new (GJS_TYPE_CONTEXT,
+                                          "search-path", search_path,
+                                          NULL);
+  g_strfreev (search_path);
 
   if (!self->gjs)
     {
@@ -129,7 +96,29 @@ ide_gjs_script_load (IdeScript *script)
     }
 
   context = ide_object_get_context (IDE_OBJECT (self));
-  set_global (self->gjs, "Context", G_OBJECT (context));
+
+  JSContext *jscontext;
+  JSObject *jsglobal;
+
+  jscontext = (JSContext *)gjs_context_get_native_context (self->gjs);
+  jsglobal = (JSObject *)gjs_get_global_object (jscontext);
+
+  JSAutoCompartment ac(jscontext, jsglobal);
+  JSAutoRequest ar(jscontext);
+  jsval jsvalue;
+
+  g_assert (IDE_IS_CONTEXT (context));
+  g_assert (jscontext);
+
+  jsvalue.setObject (*gjs_object_from_g_object (jscontext, G_OBJECT (context)));
+
+  gjs_context_eval (self->gjs, init_js_code, strlen(init_js_code), "<init>", NULL, NULL);
+
+  if (!JS_SetProperty (jscontext, jsglobal, "Context", &jsvalue))
+    {
+      g_warning (_("Failed to set IdeContext in JavaScript runtime."));
+      return;
+    }
 
   if (!gjs_context_eval (self->gjs, contents, len, path, &exit_status, &error))
     {
@@ -137,7 +126,11 @@ ide_gjs_script_load (IdeScript *script)
       return;
     }
 
-  g_info ("GJS script \"%s\" loaded.", path);
+  if (old_current != NULL)
+    {
+      gjs_context_make_current (NULL);
+      gjs_context_make_current (old_current);
+    }
 }
 
 static void
@@ -151,75 +144,88 @@ ide_gjs_script_finalize (GObject *object)
 {
   IdeGjsScript *self = (IdeGjsScript *)object;
 
-  g_clear_object (&self->file);
+  g_clear_object (&self->gjs);
 
   G_OBJECT_CLASS (ide_gjs_script_parent_class)->finalize (object);
 }
 
 static void
-ide_gjs_script_get_property (GObject    *object,
-                             guint       prop_id,
-                             GValue     *value,
-                             GParamSpec *pspec)
+ide_gjs_script_class_init (IdeGjsScriptClass *klass)
 {
-  IdeGjsScript *self = IDE_GJS_SCRIPT (object);
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeScriptClass *script_class = IDE_SCRIPT_CLASS (klass);
 
-  switch (prop_id)
-    {
-    case PROP_FILE:
-      g_value_set_object (value, ide_gjs_script_get_file (self));
-      break;
+  object_class->finalize = ide_gjs_script_finalize;
 
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
-    }
+  script_class->load = ide_gjs_script_load;
+  script_class->unload = ide_gjs_script_unload;
+}
+
+static void
+ide_gjs_script_init (IdeGjsScript *self)
+{
 }
 
 static void
-ide_gjs_script_set_property (GObject      *object,
-                             guint         prop_id,
-                             const GValue *value,
-                             GParamSpec   *pspec)
+ide_gjs_script_init_async (GAsyncInitable      *initable,
+                           gint                 io_priority,
+                           GCancellable        *cancellable,
+                           GAsyncReadyCallback  callback,
+                           gpointer             user_data)
 {
-  IdeGjsScript *self = IDE_GJS_SCRIPT (object);
+  IdeGjsScript *self = (IdeGjsScript *)initable;
+  g_autoptr(GTask) task = NULL;
+  g_autoptr(gchar) path = NULL;
+  GFile *file;
 
-  switch (prop_id)
+  g_return_if_fail (IDE_IS_GJS_SCRIPT (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = g_task_new (self, cancellable, callback, user_data);
+
+  file = ide_script_get_file (IDE_SCRIPT (self));
+
+  if (!file)
     {
-    case PROP_FILE:
-      ide_gjs_script_set_file (self, (GFile *)g_value_get_object (value));
-      break;
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_FILENAME,
+                               _("The filename for the script was not provided."));
+      return;
+    }
+
+  path = g_file_get_path (file);
 
-    default:
-      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+  if (!path)
+    {
+      g_task_return_new_error (task,
+                               G_IO_ERROR,
+                               G_IO_ERROR_INVALID_FILENAME,
+                               _("The script must be on a local filesystem."));
+      return;
     }
+
+  ide_script_load (IDE_SCRIPT (self));
+
+  g_task_return_boolean (task, TRUE);
 }
 
-static void
-ide_gjs_script_class_init (IdeGjsScriptClass *klass)
+static gboolean
+ide_gjs_script_init_finish (GAsyncInitable  *initable,
+                            GAsyncResult    *result,
+                            GError         **error)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
-  IdeScriptClass *script_class = IDE_SCRIPT_CLASS (klass);
+  GTask *task = (GTask *)result;
 
-  object_class->finalize = ide_gjs_script_finalize;
-  object_class->get_property = ide_gjs_script_get_property;
-  object_class->set_property = ide_gjs_script_set_property;
+  g_return_val_if_fail (IDE_IS_GJS_SCRIPT (initable), FALSE);
+  g_return_val_if_fail (G_IS_TASK (task), FALSE);
 
-  script_class->load = ide_gjs_script_load;
-  script_class->unload = ide_gjs_script_unload;
-
-  gParamSpecs [PROP_FILE] =
-    g_param_spec_object ("file",
-                         _("File"),
-                         _("The file containing the script contents."),
-                         G_TYPE_FILE,
-                         (GParamFlags)(G_PARAM_READWRITE |
-                                       G_PARAM_CONSTRUCT_ONLY |
-                                       G_PARAM_STATIC_STRINGS));
-  g_object_class_install_property (object_class, PROP_FILE,
-                                   gParamSpecs [PROP_FILE]);
+  return g_task_propagate_boolean (task, error);
 }
 
 static void
-ide_gjs_script_init (IdeGjsScript *self)
+async_initable_iface_init (GAsyncInitableIface *iface)
 {
+  iface->init_async = ide_gjs_script_init_async;
+  iface->init_finish = ide_gjs_script_init_finish;
 }
diff --git a/libide/ide.c b/libide/ide.c
index 059d833..2a2d37a 100644
--- a/libide/ide.c
+++ b/libide/ide.c
@@ -32,6 +32,7 @@
 #include "ide-gca-service.h"
 #include "ide-git-vcs.h"
 #include "ide-gsettings-file-settings.h"
+#include "ide-gjs-script.h"
 
 static gboolean     gProgramNameRead;
 static const gchar *gProgramName = "libide";
@@ -93,6 +94,11 @@ ide_init_ctor (void)
                                   IDE_LANGUAGE_EXTENSION_POINT".c",
                                   -100);
 
+  g_io_extension_point_implement (IDE_SCRIPT_EXTENSION_POINT,
+                                  IDE_TYPE_GJS_SCRIPT,
+                                  IDE_SCRIPT_EXTENSION_POINT".gjs",
+                                  -100);
+
   g_io_extension_point_implement (IDE_SERVICE_EXTENSION_POINT,
                                   IDE_TYPE_CLANG_SERVICE,
                                   IDE_SERVICE_EXTENSION_POINT".clang",


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