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…
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
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.
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 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
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.
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
The plugin info object contains all the information about a plugin. It is
available even when the plugin is not loaded.
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!
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
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
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
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.
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,
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!