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:
- tell yourself what the plugin is about - remember that it should do one thing and do it right
- write down the minimal feature set to know when it’s time for the first release
- try to improve an existing package before adding another
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:
- Creating Sublime Text 3 Plugins – Part 1
- Sublime Text 3 Documentation
- Sublime Text Unofficial Documentation
- API Reference
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:
- create reloader
- include reloader
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.