2

How can I develop an Extension for Code. I want to recreate atom's pigments package into Code.

1

This question is definitely not as trivial as one would think, as there's virtually no documentation and you have to feel your way around the Code sources itself.

NOTE: In this example I'll demonstrate the bare minimum in Vala that compiles, links dynamically against the Code core library and loads into Code with no problems. There is definitely some room for improvements here, as I'm only a beginner when it comes to Meson and Vala things.

The preparations

First thing you have to do is to grab a source release from here. Download any required dependencies listed in the README.md, then build it.

Now create your plugin project as usual, with the directory structure as follows:

.
├── src/
├── lib/
└── vapi/

Now we need codecore.vapi and codecore.h for the built library libcodecore.
Go to your <directory with editor's source>/build/src and copy over the codecore.vapi and codecore.h into your project's vapi/ directory.

Now copy over the libcodecore.so.0.0 from the same directory into your project's lib/ directory, but rename it to libcodecore.so instead.

Next, create a meson.build with the following content:

project('helloworldplugin', 'vala', 'c')

module_name = 'helloworldplugin'
cp = find_program('cp')

vapi_dir = join_paths(meson.current_source_dir(), 'vapi')
add_project_arguments(['--vapidir', vapi_dir], language: 'vala')

libcodecore = meson.get_compiler('c').find_library('codecore', dirs: join_paths(meson.current_source_dir(), 'lib'))

shared_library(
    meson.project_name(),
    'src/helloworldplugin.vala',
    dependencies: [
        dependency('glib-2.0'),
        dependency('gobject-2.0'),
        dependency('gtk+-3.0'),
        dependency('granite'),
        dependency('gtksourceview-3.0'),
        dependency('libpeas-1.0'),
        libcodecore
    ],
    include_directories: include_directories('vapi'),
    vala_args: ['--pkg=codecore'], # https://github.com/mesonbuild/meson/issues/2103
)

custom_target(
    module_name + '.plugin',
    input: join_paths('src', module_name + '.plugin'),
    output: module_name + '.plugin',
    command: [cp, '@INPUT@', join_paths(meson.current_build_dir(), '@OUTPUT@')],
    install: true,
    install_dir: ''
)

At this point we have everything we need to start writing actual plugin.

The plugin

Create a file called helloworldplugin.vala in your project's src/ directory, with the following contents:

namespace HelloWorld {
    public class HelloWorldPlugin : Peas.ExtensionBase, Peas.Activatable {
        // Assigned by Code at runtime, has to be casted to plugin interface object below.
        public Object object { owned get; construct; }

        Scratch.Services.Interface plugins;
        Scratch.MainWindow main_window;

        public void update_state() { return; }

        public void activate()
        {
            plugins = (Scratch.Services.Interface)object;
            plugins.hook_window.connect((window) => {
                this.main_window = window;
            });

            print("hello, world - activate\n");
        }

        public void deactivate()
        {
            print("hello, world - deactivate\n");
        }
    }
}

// Gets picked up by libpeas engine.
[ModuleInit]
public void peas_register_types(GLib.TypeModule module) {
    var objmodule = module as Peas.ObjectModule;
    objmodule.register_extension_type(typeof(Peas.Activatable),
                                      typeof(HelloWorld.HelloWorldPlugin));
}

Now we need some plugin metadata. Create a file and name it helloworldplugin.plugin with the following contents:

[Plugin]
Name=Hello world plugin
Module=helloworldplugin
Loader=C
IAge=2
Description=Bare-bones hello world plugin
Authors=Someone
Copyright=Nope
Website=https://your.url.here/

Note that Module entry value needs to match your what's between lib and .so in the output file that we'll have after this step.

The build and installation

At last! We can build the project. Go to the project's root, execute the following command: meson build && cd build && ninja.

You'll end up inside the build directory created by Meson.
Code loads its plugins from /usr/lib/x86_64-linux-gnu/io.elementary.code/plugins/, which is only writeable as root. I am not aware of any other directories Code looks for to load plugins from. I would love for someone to prove me wrong on this, though.

All we have to do now is to create a directory called helloworldplugin in the above- mentioned directory, copy over the helloworldplugin.plugin and helloworldplugin.so into that created directory, and we're done. It needs to be done with root privileges:

sudo mkdir /usr/lib/x86_64-linux-gnu/io.elementary.code/plugins/helloworldplugin
sudo cp helloworldplugin.plugin /usr/lib/x86_64-linux-gnu/io.elementary.code/plugins/helloworldplugin/
sudo cp helloworldplugin.so /usr/lib/x86_64-linux-gnu/io.elementary.code/plugins/helloworldplugin/

The results

To see the results of our work, you need to launch Code from the command line - io.elementary.code is the command.

Now go to Preferences -> Extensions and toggle your plugin on/off. Console output should be similar to this:

hello, world - activate
hello, world - deactivate

Remarks

If you want to spare yourself the typing and get straight to the meat of it, here's the GitHub repository

  • Awesome! Thanks for the detailed reply. I will try and feel my way around code. Appreciate it! – Carlos Flores Aug 10 at 3:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.