Sublime Text plugin development basics

Sublime Text is amazing editor. It shines at its speed, file search and multiple cursors. If some functionality is missing, you can easily extend it via plugins.

I have written TestRSpec plugin for running RSpec specs. Give it a try and provide me feedback via GitHub issues.

Why your own plugin?

There are usually two reasons:

  • no plugin does what you want
  • existing plugin is buggy

For rspec there’s RubyTest plugin, it supports many testing tools - minitest, rspec, cucumber. Sadly it’s not optimized for rspec. My attempt to contribute got nowhere - the code is hard to understand and navigate. Lessons learned about the plugin design:

  • maintainability - others should easily understand the code and contribute
  • low complexity - plugin does one thing and does it right

Before we start - let’s see what are the challenges.

Challenges

  • python syntax and modules - be sure to dive into modules documentation if you are writing your first python project
  • sublime api is poorly documented - you need to learn from source code and examples, as well as figure out best practices
  • a lot of conventions - higher learning curve
  • hard to debug
  • no source autoreload once source is distributed in modules

Sounds like a challenge, but it’s a lot of fun.

Plan it

Time to leave your footprint in the Sublime plugins ecosystem. Let’s make sure that you get it right:

Pick a name

You want to be found in the Package Control. Think of keywords people use when searching for your plugin. Follow the naming guidelines to avoid renaming it later.

Decide which Sublime Text version to support

Sublime Text 2 runs python 2.7 and has a slightly different API.

Sublime Text 3 runs python 3 and has a more strict (as well as more predictable) API.

Start by supporting Sublime Text 3 only, as most people are using it.

Read documentation

Sublime plugins are based on conventions - from a way to invoke commands, to a way to specify user configuration as well as key bindings. Good sources:

Sadly there is no thorough documentation source that would cover basic needs. No worries - there are a lot of well-written plugins, as well as an active community in forums to help you.

Dig into Sublime Default package source

Default Sublime package has a lot of nice examples, such as asyc processing with output stream. That’s used by exec command which launches console command and streams the output to panel.

There is no GitHub repository for the package, but there is a way to extract the code:

  • Install PackageResourceViewer
  • Extract package (follow readme instructions)
  • Open packages folder - “Preferences -> Browse Packages” and locate the source

Or just open this gist.

Plugin code reloading

Plugin code is reloaded on preferences and main python source files save. If your main source file loads other files that are in a directory, those modules are not reloaded. There’s a way to hack it:

import sys, os.path, imp, sublime, sublime_plugin

BASE_PATH = os.path.abspath(os.path.dirname(__file__))
CODE_DIRS = [
  'plugin_helpers',
  'rspec',
]
sys.path += [BASE_PATH] + [os.path.join(BASE_PATH, f) for f in CODE_DIRS]

### =======
### reload plugin files on change
if 'plugin_helpers.reloader' in sys.modules:
  imp.reload(sys.modules['plugin_helpers.reloader'])
import plugin_helpers.reloader

You can also reload code on plugin file save. It saves the main plugin file, waits a bit for reload to trigger and comes back to the file you were editing.

Third-party module usage

You can only import modules from the Python Standard Library and use the ones provided by Sublime Text.

If you want to import a third-party module, e.g. memoized, you need to include it as a dependency.

So you find the module source, copy it and publish via PackageControl.

Then add it to your project using dependencies.json file.

Thanks to 1dleberg for the tip.

Tests

There is no API or DSL to mock Sublime API calls.

It’s possible to write unit tests using python tools, there’s no easy way to write the integration ones.

Context

It is convenient to store execution context, such as current view and configuration, in a context object. It provides access to project and plugin configuration.

Package Settings

You can define custom package

Those should be placed in plugin root, names follow conventions. Settings can be structured by platform, read more.

Running shell commands

You can launch shell commands manually. This would require managing:

  • threading to not block UI
  • output stream

Luckily there is exec command, read more in unofficial documentation.

Packaging

Once you think that your plugin is ready for release, you need to test it.

  • create package file (Tools -> Command Palette -> PackageControl: Create Package File -> select your package)
  • move your developed package source out of sublime packages
  • move created package file to sublime packages
  • restart sublime
  • test if package works

You’ve done a great job if it works on the first try.

Releasing

Once the bundled package works, follow package release instructions.

It takes 1-2 weeks for the PR to be accepted.

After you make fixes and updates, release new version and package control will pick it up in a few hours.

Wrap up

Heads up in writing your first Sublime plugin. Once you are done writing, it’s time to market it. Share with your colleagues, write on Twitter, embrace issues and feature requests.

And while you are waiting for people to get excited, watch Christopher Chedeau talk about Being Successful at Open Source.