Tuesday, 20 May 2014

Writing a C++ Completion Provider for GtkSourceView

Over the last few weeks I've been implementing Python code completion in my homebrew text editor. After a lot of work I have most of the backend code worked out, the file parsing and indexing generally works.

The next step was to create a CompletionProvider to tell the Gtk+ text view about any suggestions that my code completion engine had. In my mind, I just had to do the following:
  1. Subclass CompletionProvider, implement the populate() function
  2. Call view().get_completion()->add_provider(my_provider);
  3. Wallow in the joy of having working code completion
However, it's not as simple as that, because the C++ GtkSourceView bindings suck, although they'd suck even less if they had some sensible documentation, or examples...


Let's first start with the subclassing, turns out you can't just subclass CompletionProvider, because if you do you get this wonderful gem of an error message:


GtkSourceView-CRITICAL **: gtk_source_completion_add_provider: assertion 'GTK_SOURCE_IS_COMPLETION_PROVIDER (provider)' failed

So, what do you do? It turns out you have to have to not only subclass CompletionProvider, but also Glib::Object, and then you have to do this in your constructor:

MyProvider::MyProvider():
    Glib::ObjectBase(typeid(MyProvider)),
    Glib::Object(),
    Gsv::CompletionProvider()
{

}

Fun times!

This magical voodoo allows you to now call add_provider, and you can just override the populate() function, job done. Right? RIGHT?

Well... no. For some reason that I can't fathom, populate() is not a virtual method. Instead, there are a load of methods with a "_vfunc" suffix which are the virtual methods you have to override. 

But of course, it's again not just a case of overriding populate_vfunc()  there's still one final thing you need to do to make your completions actually show up, and that's to override get_activation_vfunc() .. something like this:

Gsv::CompletionActivation Provider::get_activation_vfunc() const {
    return Gsv::COMPLETION_ACTIVATION_INTERACTIVE | Gsv::COMPLETION_ACTIVATION_USER_REQUESTED;
}

Now, provided that your population function is coded correctly, you should see completions in your editor. It took me hours to figure this out. 

So here you fair traveller of the Interwebs, I note this down for eternity so that no other befalls the same fate as I.

Update: One other thing. When you override populate_vfunc, make sure you always call add_proposals, even if you are passing an empty vector... otherwise you'll find that you can't hit enter in your editor because... I dunno, it's just broken.

1 comment:

  1. This comment has been removed by a blog administrator.

    ReplyDelete