Steve Frécinaux

It’s a matter of plugins…

A brief history of plugin support in Gedit (and Gnome)

Gedit is one of the oldest softwares of the Gnome fame, and it got a major overhaul in time for Gnome 2.14. This was an eternity ago: the new mdi branch was merged in late 2005. This nearly full rewrite dropped many then obsolete dependencies (BonoboUI, popt) and introduced many innovations over the previous versions, some of those spreading into the whole Gnome project.

One of the best known innovations was a replacement for all those annoying dialog boxes that popped for very good reasons at the worst possible moment. Understanding that those messages were very contextual in nature (they were always related to a particular document in some tab), the dialog boxes were replaced by coloured message areas embedded directly in the tab. This brand new idea got used by several other applications, and eventually got into Gtk+ lately under the name “GtkInfoBar”.

Another of those innovations was the brand new plugin capabilities it offered. At that time, it was possible to extend Gedit with C plugins for a long time, but with Gedit 2.14 came a few new capabilities:

Object-Oriented Plugins. From then on, plugins were objects that inherited from a base plugin class. Overriding virtual methods became the standard way of defining the plugin behaviour. And this was nice, as more and more people are familiar with object-oriented programming these days.

Python Plugins. PyGTK was in a very good state and Epiphany, the web browser everyone likes, was already using it to give access to its internals to python developpers. Gedit borrowed it and extended it to allow writing plugins as python objects as well.

This plugins engine has been very successful, and a lot of specialised plugins appeared in a few years. It was also transplanted to a lot of other Gnome applications (Eye of Gnome, Rhythmbox and Totem to cite the most prominent ones) but we never made a library out of it: creating and maintaining python bindings was time consuming and tedious, and it didn’t make sense to have a library if every users of it would have to duplicate half of the code anyway.

But things have changed…

Thanks to the recent gobject-introspection project, “immediate” bindings are flourishing. Out of a single easily generated introspection dataset, it is now possible to benefit from full bindings for several languages, including Python (through PyGI) and the new kid in town, Javascript (through Seed or GJS). In total contrast with the old “PyGTK way”, bindings are now generated in minutes and require close to no maintainance and very few specific code. This fact alone creates the ideal condition for a librarified version of the Gedit plugins engine.

Introducing libpeas

This is why I am very proud to announce the first release of libpeas. libpeas is the next evolution of the Gedit plugins engine, and is targetted at giving every application the chance to assume its own extensibility. It also has a set of enhanced features with regard to what Gedit used to provide, mirroring the desiderata of your favourite text editor’s developpers.

Multiple extension points. One of the most frustrating limitations of the Gedit plugins engine was that it only allows extending a single class, called GeditPlugin. With libpeas, this limitation vanishes, and the application writer is now able to provide a set of GInterfaces the plugin writer will be able to implement as his plugin requires.

On-demand programming language support. libpeas comes with a set of supported languages (currently, C, Python and Javascript). Those languages are supported through “loaders” which are loaded on demand. What it means is that you only pay for what you use: if you have no Python plugin, the Python interpreter won’t be loaded in memory. Of course, the same goes for the C and for the Seed/JS loader.

Damn simple to use (or at least we try hard). Adding support for libpeas-enabled plugins in your own application is a matter of minutes. You only have to create an instance of the plugins engine, and call methods on the implementations of the various extension points. That’s it, no clock harmed.

A shared library for everyone. As I noted earlier, the latest improvements of our beloved development platform made it possible to create bindings for apps very quickly. And with the Gnome 3 announcement, this looked like the perfect timing to make the plugins engine and its latest improvements available to everyone, with a library. Also, hopefully it will reduce code duplication and allow bugs to be fixed at the right place, once and for all, improving the quality of our applications.

As a member of the Gnome community and as an offspring of gedit, libpeas already shares most of the Gnome infrastructure and philosophy:

  • You can download the first release tarball on the Gnome FTP servers.
  • You can browse the source and contribute using our git repository.
  • You can come and discuss with me and others on #libpeas (GimpNet).
  • And you can report bug or propose new features through the good old Gnome Bugzilla, against the libpeas module.

Many many thanks for their support to those who made this possible, and especially to the current very active gedit team (Paolo Borelli, Jesse van den Kieboom, Ignacio Casal Quinteiro and Garett Regier).

A few hints on using libpeas

As always for the new projects, it can take some time to grasp all the subtleties at first.

Plugins versus Extensions.

Something that is going to puzzle most of the newcomers is the fact that the libpeas API talks about both plugins and extensions, two terms that are usually used interchangeably, but who have very different meanings in libpeas.

Let’s try and give a definition of both of these words in this context:

Plugin. In the context of libpeas, a plugin is a logical package. It’s what you will enable or disable from the UI, and at the end it is what the user will see. An example of plugin for gedit would be the file browser plugin.

Extension. An extension is an object which implements an interface associated to an extension point. There can be several extensions in a single plugin. Examples of extensions provided by the file browser plugin would be the configuration dialog, the left panel pane and a completion provider for file names.

Building libpeas

Building libpeas is quite straightforward. But you need to be careful to build pygobject with the --enable-pygi option if you plan on using the Python bindings capability, or you will experience weird bugs.

Sample code

The libpeas package contains a sample application called peas-demo, and sample plugins written in C, Python and Javascript.

I’m not going to cut and paste a lot of code here, but the global idea is this one: you create a new PeasEngine instance and give it the information needed for it to find your plugins. Then you load some plugins (you can use the PeasUIPluginManager for that purpose) and perform actions through some PeasExtensions objects you can get from the engine.

PeasEngine. The engine is the main object for the libpeas integration. It will handle the loading and unloading of the various plugins, and it will give you PeasExtensions.

PeasPluginInfo. The plugin info object contains all the information about a plugin. It is available even when the plugin is not loaded.

PeasExtension. This is an extension, as seen from the application point of view. It is actually a proxy to the real extension, and provides a single peas_extension_call method that will allow you to call a method of the extension by name. The reason why it is a proxy instead of being the actual extension from the plugin is that some of the bindings don’t support GObject subclassing (and don’t plan to). Also, not using the actual object directly avoids quite a lot of hard-to-debug reference issues. Thrust me on that, I tried!

PeasExtensionSet. This is an automatically updated group of PeasExtensions. At any moment of time, a PeasExtensionSet instance will contain one PeasExtension for each plugin loaded that provides the target extension.

PeasActivatable and PeasUIConfigurable. Those are the two current built-in extension interfaces. The former defines an object which will be activated on object creation and deactivated on object destruction, while the latter is used by the embedded plugin manager UI, to provide a configuration dialog. You can of course provide your own extension interfaces!

What’s next?

We are now planning on porting a few apps to libpeas in the following weeks. Several contributors already marked their interest in libpeas for the application they contribute to, and I would like myself to see our mighty text editor ported as well. I guess I will be posting more announcements for ported apps and niceties as time passes.

But libpeas is far from a finished project yet. Below are a few tracks for future development of libpeas. Don’t hesitate to jump in!

Great documentation. The current documentation for libpeas is, well, the bare minimum. There is a quite complete reference documentation for the API, but there is no howto, no implementation guide for the plugins in various languages, etc. And a good documentation contributes a lot to making a good library.

Named extension points. Extension points are currently defined by interface types. While this works great for many use cases (Nautilus has done this for ages), it’s actually quite limitative sometimes. For instance, the PeasActivatable interface, which defines an extension that can be activated, and then deactivated on objects of a certain type, could easily be re-used in different contexts: windows, tabs, documents. So you can easily see that named extension points here would be a nice feature.

Construction-time properties. Currently, extensions are instanciated with no dynamic arguments. But in some cases, it would be nice if we were able to give some arguments at instanciation time, through GObject properties. For instance, PeasActivatable extensions are usually related to a single object, but currently this object is give as an argument for each of the methods of the interface. Passing it to the extension at creation time and removing that argument from the method prototypes would be a nice touch.

More automatisms. With the rise of GSettings, it would now be possible for libpeas to manage itself the storage of the plugins state. It would then make sense to deal ourselves with “invisible” and “autoloaded” plugins, which are currently left at the application discretion.

Vala and C++ support. Adding support for those should not be hard, and would turn out to be very low cost as both are compiled languages and would make use of the existing loader. Totem already has Vala support, too, and the C loader has been thought with C++ support in mind, thanks to Debarshi Ray.

Guile support. This might be the nasty secret desire of mine, but I’ve always found Scheme support for plugins appealing. And this might well become true if sbank succeeds, and with Andy Wingo’s support… Andy?

So, here is the current status of libpeas. Now, download it, try it, and come and join us on #libpeas to share your thoughts!