void ide_application_load_plugins (IdeApplication *self) { PeasEngine *engine; const GList *list; g_return_if_fail (IDE_IS_APPLICATION (self)); engine = peas_engine_get_default (); list = peas_engine_get_plugin_list (engine); for (; list; list = list->next) { PeasPluginInfo *plugin_info = list->data; GSettings *settings; const gchar *module_name; module_name = peas_plugin_info_get_module_name (plugin_info); settings = _ide_application_plugin_get_settings (self, module_name); g_object_set_data (G_OBJECT (settings), "PEAS_PLUGIN_INFO", plugin_info); g_signal_connect_object (settings, "changed::enabled", G_CALLBACK (ide_application_plugins_enabled_changed), self, G_CONNECT_SWAPPED); if (!g_settings_get_boolean (settings, "enabled")) continue; /* * If we are running the unit tests, we don't want to load plugins here, * but defer until the test is loading to perform the loading. However, * we do want all of the other machinery above to be setup. */ if (self->mode == IDE_APPLICATION_MODE_TESTS) continue; if (ide_application_can_load_plugin (self, plugin_info)) { g_debug ("Loading plugin \"%s\"", peas_plugin_info_get_module_name (plugin_info)); peas_engine_load_plugin (engine, plugin_info); } } }
static void ide_keybindings_load_plugin (IdeKeybindings *self, PeasPluginInfo *plugin_info, PeasEngine *engine) { g_autofree gchar *path = NULL; const gchar *module_name; g_autoptr(GBytes) bytes = NULL; g_autoptr(GtkCssProvider) provider = NULL; g_assert (IDE_IS_KEYBINDINGS (self)); g_assert (plugin_info != NULL); g_assert (PEAS_IS_ENGINE (engine)); if (!self->mode || !self->plugin_providers) return; module_name = peas_plugin_info_get_module_name (plugin_info); path = g_strdup_printf ("/org/gnome/builder/plugins/%s/keybindings/%s.css", module_name, self->mode); bytes = g_resources_lookup_data (path, 0, NULL); if (bytes == NULL) return; IDE_TRACE_MSG ("Loading %s keybindings for \"%s\" plugin", self->mode, module_name); provider = gtk_css_provider_new (); gtk_css_provider_load_from_resource (provider, path); gtk_style_context_add_provider_for_screen (gdk_screen_get_default (), GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION + 1); g_hash_table_insert (self->plugin_providers, g_strdup (module_name), g_steal_pointer (&provider)); }
static void ide_application_unload_plugin_resources (IdeApplication *self, PeasPluginInfo *plugin_info, PeasEngine *engine) { g_autofree gchar *path = NULL; const gchar *data_dir; const gchar *module_name; g_assert (IDE_IS_APPLICATION (self)); g_assert (plugin_info != NULL); g_assert (PEAS_IS_ENGINE (engine)); module_name = peas_plugin_info_get_module_name (plugin_info); data_dir = peas_plugin_info_get_data_dir (plugin_info); /* Remove embedded gresources */ path = g_strdup_printf ("resource:///org/gnome/builder/plugins/%s/", module_name); dzl_application_remove_resources (DZL_APPLICATION (self), path); /* Remove on disk resources */ if (!g_str_has_prefix (data_dir, "resource://")) dzl_application_remove_resources (DZL_APPLICATION (self), data_dir); ide_application_plugins_unload_plugin_gresources (self, plugin_info, engine); }
static PeasPluginInfo * ide_application_locate_worker (IdeApplication *self, const gchar *worker_name) { PeasEngine *engine; const GList *list; g_assert (IDE_IS_APPLICATION (self)); g_assert (worker_name != NULL); engine = peas_engine_get_default (); list = peas_engine_get_plugin_list (engine); for (; list != NULL; list = list->next) { PeasPluginInfo *plugin_info = list->data; const gchar *name; name = peas_plugin_info_get_module_name (plugin_info); if (g_strcmp0 (name, worker_name) == 0) return plugin_info; } return NULL; }
static gboolean peas_plugin_loader_python_load (PeasPluginLoader *loader, PeasPluginInfo *info) { PeasPluginLoaderPython *pyloader = PEAS_PLUGIN_LOADER_PYTHON (loader); PeasPluginLoaderPythonPrivate *priv = GET_PRIV (pyloader); const gchar *module_dir, *module_name; PyObject *pymodule; PyGILState_STATE state = PyGILState_Ensure (); module_dir = peas_plugin_info_get_module_dir (info); module_name = peas_plugin_info_get_module_name (info); pymodule = peas_python_internal_call ("load", &PyModule_Type, "(sss)", info->filename, module_dir, module_name); if (pymodule != NULL) { info->loader_data = pymodule; priv->n_loaded_plugins += 1; } PyGILState_Release (state); return pymodule != NULL; }
static gboolean ide_application_can_load_plugin (IdeApplication *self, PeasPluginInfo *plugin_info) { const gchar *module_name; g_assert (IDE_IS_APPLICATION (self)); g_assert (plugin_info != NULL); module_name = peas_plugin_info_get_module_name (plugin_info); for (guint i = 0; i < G_N_ELEMENTS (blacklisted_plugins); i++) { if (g_strcmp0 (module_name, blacklisted_plugins[i]) == 0) return FALSE; } if (self->mode == IDE_APPLICATION_MODE_WORKER) { if (self->worker != plugin_info) return FALSE; } if (self->mode == IDE_APPLICATION_MODE_TOOL) { /* * Plugins might provide critical features needed * to load a project (build system, vcs, etc). */ return TRUE; } /* * TODO: Do ABI check on external data. * * Right now, we don't have any way to check that the plugin is implementing * the same version of the API/ABI that the application exports. There are a * couple ways we could go about doing this. * * One approach might be to generate UUIDs for each plugin structure, * and update it every time the structure changes. However, plenty of changes * can be safe for existing modules. So perhaps we need something that has * a revision since last break. Then plugins would specify which version * and revision of an interface they require. * * Imagine the scenario that FooIface added the method frobnicate(). Previous * extensions for FooIface are perfectly happy to keep on working, but a new * addin that requires FooIface may require frobnicate()'s existance. So while * the ABI hasn't broken, some plugins will require a newer revision. * * This is not entirely different from libtool's interface age. Presumably, * Gedit's IAge is similar here, but we would need it per-structure. */ return TRUE; }
static gchar * get_script_filename_for_plugin_info (PeasPluginInfo *info) { gchar *basename; gchar *filename; basename = g_strconcat (peas_plugin_info_get_module_name (info), ".js", NULL); filename = g_build_filename (peas_plugin_info_get_module_dir (info), basename, NULL); g_free (basename); return filename; }
static void ide_workbench_addin_added (PeasExtensionSet *set, PeasPluginInfo *plugin_info, PeasExtension *extension, gpointer user_data) { IdeWorkbench *self = user_data; g_assert (PEAS_IS_EXTENSION_SET (set)); g_assert (plugin_info != NULL); g_assert (IDE_IS_WORKBENCH_ADDIN (extension)); g_assert (IDE_IS_WORKBENCH (self)); IDE_TRACE_MSG ("Loading workbench addin for %s", peas_plugin_info_get_module_name (plugin_info)); ide_workbench_addin_load (IDE_WORKBENCH_ADDIN (extension), self); }
static gchar * get_script_for_plugin_info (PeasPluginInfo *info, SeedContext context) { gchar *basename; gchar *filename; gchar *script = NULL; basename = g_strconcat (peas_plugin_info_get_module_name (info), ".js", NULL); filename = g_build_filename (peas_plugin_info_get_module_dir (info), basename, NULL); g_debug ("Seed script filename is '%s'", filename); g_file_get_contents (filename, &script, NULL, NULL); g_free (basename); g_free (filename); return script; }
static void ide_preferences_builtin_register_plugins (IdePreferences *preferences) { PeasEngine *engine; const GList *list; guint i = 0; g_assert (IDE_IS_PREFERENCES (preferences)); engine = peas_engine_get_default (); list = peas_engine_get_plugin_list (engine); ide_preferences_add_page (preferences, "plugins", _("Extensions"), 700); ide_preferences_add_list_group (preferences, "plugins", "installed", _("Installed Extensions"), 0); ide_preferences_add_list_group (preferences, "plugins", "builtin", _("Bundled Extensions"), 100); for (; list; list = list->next, i++) { g_autofree gchar *path = NULL; PeasPluginInfo *plugin_info = list->data; const gchar *desc; const gchar *name; const gchar *group; if (peas_plugin_info_is_hidden (plugin_info)) continue; name = peas_plugin_info_get_name (plugin_info); desc = peas_plugin_info_get_description (plugin_info); path = g_strdup_printf ("/org/gnome/builder/plugins/%s/", peas_plugin_info_get_module_name (plugin_info)); if (peas_plugin_info_is_builtin (plugin_info)) group = "builtin"; else group = "installed"; ide_preferences_add_switch (preferences, "plugins", group, "org.gnome.builder.plugin", "enabled", path, NULL, name, desc, NULL, i); } }
void ide_application_init_plugin_accessories (IdeApplication *self) { const GList *list; PeasEngine *engine; g_assert (IDE_IS_APPLICATION (self)); self->plugin_gresources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)g_resource_unref); engine = peas_engine_get_default (); g_signal_connect_object (engine, "load-plugin", G_CALLBACK (ide_application_load_plugin_resources), self, G_CONNECT_AFTER | G_CONNECT_SWAPPED); g_signal_connect_object (engine, "unload-plugin", G_CALLBACK (ide_application_unload_plugin_resources), self, G_CONNECT_SWAPPED); list = peas_engine_get_plugin_list (engine); for (; list != NULL; list = list->next) { PeasPluginInfo *plugin_info = list->data; const gchar *module_name; GSettings *settings; module_name = peas_plugin_info_get_module_name (plugin_info); settings = _ide_application_plugin_get_settings (self, module_name); if (!g_settings_get_boolean (settings, "enabled")) continue; ide_application_load_plugin_resources (self, plugin_info, engine); } }
static void ide_application_plugins_unload_plugin_gresources (IdeApplication *self, PeasPluginInfo *plugin_info, PeasEngine *engine) { const gchar *module_name; GResource *resources; g_assert (IDE_IS_APPLICATION (self)); g_assert (plugin_info != NULL); g_assert (PEAS_IS_ENGINE (engine)); module_name = peas_plugin_info_get_module_name (plugin_info); resources = g_hash_table_lookup (self->plugin_gresources, module_name); if (resources != NULL) { g_resources_unregister (resources); g_hash_table_remove (self->plugin_gresources, module_name); } }
static void ide_keybindings_unload_plugin (IdeKeybindings *self, PeasPluginInfo *plugin_info, PeasEngine *engine) { GtkStyleProvider *provider; const gchar *module_name; g_assert (IDE_IS_KEYBINDINGS (self)); g_assert (plugin_info != NULL); g_assert (PEAS_IS_ENGINE (engine)); if (self->plugin_providers == NULL) return; module_name = peas_plugin_info_get_module_name (plugin_info); provider = g_hash_table_lookup (self->plugin_providers, module_name); if (provider == NULL) return; gtk_style_context_remove_provider_for_screen (gdk_screen_get_default (), provider); g_hash_table_remove (self->plugin_providers, module_name); }
static void ide_application_plugins_load_plugin_gresources (IdeApplication *self, PeasPluginInfo *plugin_info, PeasEngine *engine) { g_autofree gchar *gresources_path = NULL; g_autofree gchar *gresources_basename = NULL; const gchar *module_dir; const gchar *module_name; g_assert (IDE_IS_APPLICATION (self)); g_assert (plugin_info != NULL); g_assert (PEAS_IS_ENGINE (engine)); module_dir = peas_plugin_info_get_module_dir (plugin_info); module_name = peas_plugin_info_get_module_name (plugin_info); gresources_basename = g_strdup_printf ("%s.gresource", module_name); gresources_path = g_build_filename (module_dir, gresources_basename, NULL); if (g_file_test (gresources_path, G_FILE_TEST_IS_REGULAR)) { g_autoptr(GError) error = NULL; GResource *resource; resource = g_resource_load (gresources_path, &error); if (resource == NULL) { g_warning ("Failed to load gresources: %s", error->message); return; } g_hash_table_insert (self->plugin_gresources, g_strdup (module_name), resource); g_resources_register (resource); } }
static void mnb_home_new_widget_dialog_init (MnbHomeNewWidgetDialog *self) { ClutterActor *table, *itemview; MnbHomeNewWidgetDialogItemFactory *factory; MnbHomePluginsEngine *engine; const GList *l; self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MNB_TYPE_HOME_NEW_WIDGET_DIALOG, MnbHomeNewWidgetDialogPrivate); /* set up model and view */ self->priv->items = clutter_list_model_new (ITEMS_N_COLUMNS, G_TYPE_STRING, "Module", /* ITEMS_MODULE */ G_TYPE_STRING, "Name", /* ITEMS_NAME */ G_TYPE_STRING, "Icon" /* ITEMS_ICON */); table = mx_table_new (); mx_bin_set_child (MX_BIN (self), table); factory = g_object_new (MNB_TYPE_HOME_NEW_WIDGET_DIALOG_ITEM_FACTORY, NULL); factory->dialog = self; itemview = mx_item_view_new (); mx_table_add_actor (MX_TABLE (table), itemview, 0, 1); mx_item_view_set_model (MX_ITEM_VIEW (itemview), self->priv->items); mx_item_view_set_factory (MX_ITEM_VIEW (itemview), MX_ITEM_FACTORY (factory)); mx_item_view_add_attribute (MX_ITEM_VIEW (itemview), "module", ITEMS_MODULE); mx_item_view_add_attribute (MX_ITEM_VIEW (itemview), "label", ITEMS_NAME); mx_item_view_add_attribute (MX_ITEM_VIEW (itemview), "icon", ITEMS_ICON); clutter_actor_show_all (table); /* find plugins */ /* FIXME: should we monitor for new plugins on the fly? */ engine = mnb_home_plugins_engine_dup (); for (l = peas_engine_get_plugin_list (PEAS_ENGINE (engine)); l != NULL; l = g_list_next (l)) { PeasPluginInfo *info = l->data; char *icon; if (!peas_plugin_info_is_available (info, NULL)) continue; icon = g_build_filename ( peas_plugin_info_get_module_dir (info), peas_plugin_info_get_icon_name (info), NULL); clutter_model_append (self->priv->items, ITEMS_MODULE, peas_plugin_info_get_module_name (info), ITEMS_NAME, peas_plugin_info_get_name (info), ITEMS_ICON, icon, -1); g_free (icon); } /* add actions */ mx_dialog_add_action (MX_DIALOG (self), mx_action_new_full ("cancel", _("Cancel"), G_CALLBACK (home_new_widget_dialog_cancel), self)); g_object_unref (engine); g_object_unref (factory); }
static gboolean peas_plugin_loader_seed_load (PeasPluginLoader *loader, PeasPluginInfo *info) { PeasPluginLoaderSeed *sloader = PEAS_PLUGIN_LOADER_SEED (loader); gchar *filename; gchar *content; GError *error = NULL; SeedContext context; SeedScript *script; SeedException exc = NULL; SeedObject global, extensions; SeedInfo *sinfo; filename = get_script_filename_for_plugin_info (info); g_debug ("Seed script filename is '%s'", filename); if (!g_file_get_contents (filename, &content, NULL, &error)) { g_warning ("Error: %s", error->message); g_error_free (error); g_free (filename); return FALSE; } context = seed_context_create (seed->group, NULL); seed_prepare_global_context (context); script = seed_make_script (context, content, filename, 0); seed_evaluate (context, script, NULL); exc = seed_script_exception (script); seed_script_destroy (script); g_free (content); g_free (filename); if (exc) { gchar *exc_string = seed_exception_to_string (context, exc); g_warning ("Seed Exception: %s", exc_string); g_free (exc_string); seed_context_unref (context); return FALSE; } global = seed_context_get_global_object (context); extensions = seed_object_get_property (context, global, "extensions"); if (seed_value_is_object (context, extensions)) { sinfo = (SeedInfo *) g_slice_new (SeedInfo); sinfo->context = context; sinfo->extensions = extensions; seed_value_protect (context, extensions); g_hash_table_insert (sloader->loaded_plugins, info, sinfo); return TRUE; } else { g_warning ("extensions is not an object in plugin '%s'", peas_plugin_info_get_module_name (info)); seed_context_unref (context); return FALSE; } }
static PeasExtension * peas_plugin_loader_seed_create_extension (PeasPluginLoader *loader, PeasPluginInfo *info, GType exten_type, guint n_parameters, GParameter *parameters) { PeasPluginLoaderSeed *sloader = PEAS_PLUGIN_LOADER_SEED (loader); SeedInfo *sinfo; SeedValue extension_ctor, extension; guint i, j; SeedValue value; GValue gvalue = { 0 }; GArray *interfaces; gchar **property_names; sinfo = (SeedInfo *) g_hash_table_lookup (sloader->loaded_plugins, info); /* FIXME: instantiate new object and pass the parameters */ extension_ctor = seed_object_get_property (sinfo->context, sinfo->extensions, g_type_name (exten_type)); if (!extension_ctor || seed_value_is_undefined (sinfo->context, extension_ctor) || seed_value_is_null (sinfo->context, extension_ctor)) return NULL; if (!seed_value_is_object (sinfo->context, extension_ctor)) { g_warning ("Extension '%s' in plugin '%s' is not a Seed object", g_type_name (exten_type), peas_plugin_info_get_module_name (info)); return NULL; } /* Instantiate the ctor object into a new specific object. */ extension = JSObjectCallAsConstructor (sinfo->context, extension_ctor, 0, NULL, NULL); if (extension == NULL) { #ifndef PEAS_DISABLE_DEPRECATED_FEATURES gchar **property_names; g_warning ("DEPRECATION WARNING: Extension '%s' in plugin '%s' is not a valid " "constructor function. Support for extension initialization by array " "copy will be dropped soon.", g_type_name (exten_type), peas_plugin_info_get_module_name (info)); extension = seed_make_object (sinfo->context, NULL, NULL); property_names = seed_object_copy_property_names (sinfo->context, extension_ctor); for (i = 0; property_names[i] != NULL; i++) { SeedValue value; value = seed_object_get_property (sinfo->context, extension_ctor, property_names[i]); seed_object_set_property (sinfo->context, extension, property_names[i], value); } g_strfreev (property_names); #else g_warning ("Extension '%s' in plugin '%s' is not a valid constructor", g_type_name (exten_type), peas_plugin_info_get_module_name (info)); return NULL; #endif } /* Set the properties as well, cannot use * g_object_set_property() because it may be construct-only */ for (i = 0; i < n_parameters; i++) { gchar *key; /* We want to normalize the property names to have a '_' instead of the * conventional '-', to make them accessible through this.property_name */ key = g_strdup (parameters[i].name); for (j = 0; key[j] != '\0'; j++) { if (key[j] == '-') key[j] = '_'; } value = seed_value_from_gvalue (sinfo->context, ¶meters[i].value, NULL); seed_object_set_property (sinfo->context, extension, key, value); g_free (key); } /* Set the plugin info as an attribute of the instance */ g_value_init (&gvalue, PEAS_TYPE_PLUGIN_INFO); g_value_set_boxed (&gvalue, info); value = seed_value_from_gvalue (sinfo->context, &gvalue, NULL); seed_object_set_property (sinfo->context, extension, "plugin_info", value); g_value_unset (&gvalue); /* Do not add exten_type as it will be added below */ interfaces = g_array_new (TRUE, FALSE, sizeof (GType)); property_names = seed_object_copy_property_names (sinfo->context, sinfo->extensions); for (i = 0; property_names[i] != NULL; ++i) { gchar *property_name = property_names[i]; SeedValue *prop_extension_ctor; GType the_type; prop_extension_ctor = seed_object_get_property (sinfo->context, sinfo->extensions, property_name); if (prop_extension_ctor != extension_ctor) continue; if (!seed_value_is_object (sinfo->context, extension_ctor)) { g_warning ("Extension '%s' in plugin '%s' is not a Seed object", property_name, peas_plugin_info_get_module_name (info)); continue; } the_type = peas_gi_get_type_from_name (property_name); if (the_type == G_TYPE_INVALID) { g_warning ("Could not find GType for '%s', " "did you forget to import it?", property_name); } else { g_array_append_val (interfaces, the_type); } } g_array_sort (interfaces, (GCompareFunc) prerequisites_sort); g_strfreev (property_names); return peas_extension_seed_new (exten_type, (GType *) g_array_free (interfaces, FALSE), sinfo->context, extension); }
void ide_application_discover_plugins (IdeApplication *self) { PeasEngine *engine = peas_engine_get_default (); const GList *list; gchar *path; g_autoptr(GError) error = NULL; g_return_if_fail (IDE_IS_APPLICATION (self)); if (g_getenv ("GB_IN_TREE_PLUGINS") != NULL) { GDir *dir; g_irepository_prepend_search_path (BUILDDIR"/src/gstyle"); g_irepository_prepend_search_path (BUILDDIR"/src/libide"); if ((dir = g_dir_open (BUILDDIR"/src/plugins", 0, NULL))) { const gchar *name; while ((name = g_dir_read_name (dir))) { path = g_build_filename (BUILDDIR, "src", "plugins", name, NULL); peas_engine_prepend_search_path (engine, path, path); g_free (path); } g_dir_close (dir); } } else { g_irepository_prepend_search_path (PACKAGE_LIBDIR"/gnome-builder/girepository-1.0"); peas_engine_prepend_search_path (engine, PACKAGE_LIBDIR"/gnome-builder/plugins", PACKAGE_DATADIR"/gnome-builder/plugins"); } /* * We have access to ~/.local/share/gnome-builder/ for plugins even when we are * bundled with flatpak, so might as well use it. */ if (ide_is_flatpak ()) { g_autofree gchar *plugins_dir = g_build_filename (g_get_home_dir (), ".local", "share", "gnome-builder", "plugins", NULL); g_irepository_prepend_search_path (plugins_dir); peas_engine_prepend_search_path (engine, plugins_dir, plugins_dir); } if (!g_irepository_require (NULL, "Ide", "1.0", 0, &error) || !g_irepository_require (NULL, "Gtk", "3.0", 0, &error) || !g_irepository_require (NULL, "Dazzle", "1.0", 0, &error)) g_warning ("Cannot enable Python 3 plugins: %s", error->message); else { /* Avoid spamming stderr with Ide import tracebacks */ peas_engine_enable_loader (engine, "python3"); } peas_engine_prepend_search_path (engine, "resource:///org/gnome/builder/plugins/", NULL); path = g_build_filename (g_get_user_data_dir (), "gnome-builder", "plugins", NULL); peas_engine_prepend_search_path (engine, path, NULL); g_free (path); list = peas_engine_get_plugin_list (engine); for (; list; list = list->next) { PeasPluginInfo *plugin_info = list->data; g_debug ("Discovered plugin \"%s\"", peas_plugin_info_get_module_name (plugin_info)); } }