So you've written a few Sublime Text plugins and now you want to take things further. Maybe your plugin is slow, complex, or you're just curious about what else you can do with Sublime's API. This article digs a bit deeper into advanced techniques, real examples, and practical performance tips -- straight from the trenches.
Everything in Sublime is built around commands and events.
Commands do things when you call them. Events let Sublime react to what you do -- like saving, typing, or switching files.
Here's a small reminder of both concepts:
import sublime
import sublime_plugin
class InsertHeaderCommand(sublime_plugin.TextCommand):
def run(self, edit):
self.view.insert(edit, 0, "# Header inserted automatically!\n")
class AutoHeaderOnSave(sublime_plugin.EventListener):
def on_post_save(self, view):
view.run_command("insert_header")
Simple, right? The beauty of Sublime's API is that small scripts like this can fundamentally change how you work.
Check the plugin reference for every hook you can use.
As soon as your code grows beyond one file, structure it properly. Create a package folder in Packages/ and use this pattern:
MyPlugin/
main.py
Default.sublime-commands
MyPlugin.sublime-settings
keymaps/
menus/
Default.sublime-commands - Defines commands for the command palette. .sublime-settings - Store user-configurable options. .python-version - Controls which Python runtime Sublime uses (3.3 or 3.8). ctrl+alt+h to commands. Learn more in the official packaging guide.
Long-running operations -- like calling APIs, reading files, or linting -- should never block the UI. Sublime provides a handy helper:
def run_linter_async(file_path):
# Simulated background work
print("Linting:", file_path)
sublime.set_timeout_async(lambda: run_linter_async("file.py"))
Async execution ensures the UI remains responsive. This is essential when hooking into high-frequency events like on_modified().
Make your plugin discoverable by adding a .sublime-commands file:
[
{
"caption": "My Plugin: Insert Header",
"command": "insert_header"
}
]
This makes it show up in the Command Palette -- no setup required.
You can interact with users directly inside Sublime!
Use quick panels, popups, and input boxes to collect input or show data.
class AskNameCommand(sublime_plugin.WindowCommand):
def run(self):
self.window.show_input_panel(
"Enter your name:", "", self.on_done, None, None
)
def on_done(self, text):
sublime.message_dialog(f"Hello, {text}!")
That tiny snippet creates a prompt and shows a message dialog -- all without leaving the editor.
You can integrate Sublime with any command‑line tool. Example: automatically run Black when saving a Python file.
import subprocess
class AutoFormatOnSave(sublime_plugin.EventListener):
def on_post_save(self, view):
file_path = view.file_name()
if file_path and file_path.endswith(".py"):
subprocess.run(["black", file_path])
sublime.status_message("Code formatted with Black!")
Be sure to handle errors gracefully -- don't assume the command always succeeds. Wrap calls in try/except, and show feedback via sublime.error_message() if something fails.
Debugging Sublime plugins isn't glamorous, but it's simple:
print() statements -- they appear in the console (`Ctrl+``).sublime.status_message() for temporary status messages.If you subscribe to events (e.g. on_modified()), remember to unsubscribe in plugin_unloaded().
Heavy event listeners can tank performance, especially on large files.
Always debounce, cache, or limit how often you run logic.
def plugin_unloaded():
print("My plugin unloaded cleanly")
When your plugin is stable:
Use semantic versioning and keep your changelog up to date. Users appreciate it.
Takeaway: Treat Sublime as a platform, not just an editor.
Plugins are your way to shape it into a development tool that fits you perfectly.