Introduction to Sublime Text Plugins
Sublime Text is one of the most popular code editors among developers, prized for its speed, minimal interface, and powerful extensibility. At the heart of this extensibility lies the plugin system, which allows developers to customize nearly every aspect of the editor. Sublime Text plugins are written in Python and can range from simple snippets to complex language servers and build systems.
Whether you want to automate repetitive tasks, add support for a new programming language, or integrate external tools into your workflow, understanding how to build Sublime Text plugins is an invaluable skill. This guide walks you through everything from the basics of plugin architecture to advanced techniques and best practices.
What Is a Sublime Text Plugin?
A Sublime Text plugin is a Python package that extends the functionality of the editor. Plugins can define commands, event listeners, syntax definitions, completions, snippets, and more. They run inside Sublime Text's embedded Python interpreter (Python 3.3 in Sublime Text 3, and Python 3.8 in Sublime Text 4).
Plugins are distributed as packages, which are essentially folders containing Python files, metadata, and optional resources like menus, key bindings, and settings. A package can be a single .py file or a complex directory structure with submodules.
Key Components of a Plugin
- Commands โ Callable actions that can be triggered via keyboard shortcuts, menus, or the command palette.
- Event Listeners โ Classes that respond to editor events like file saving, modification, or loading.
- Settings โ JSON configuration files that users can customize.
- Key Bindings โ Mappings between keyboard shortcuts and commands.
- Syntax Definitions โ Rules for highlighting and scoping text in specific file types.
- Snippets โ Reusable code templates.
Why Plugins Matter
Sublime Text ships with a robust set of features, but every developer's workflow is unique. Plugins bridge the gap between a generic editor and a personalized development environment. Here are some reasons why plugins matter:
- Productivity โ Automate repetitive tasks like formatting, linting, or inserting boilerplate code.
- Language Support โ Add syntax highlighting, autocompletion, and build systems for niche or new languages.
- Integration โ Connect Sublime Text to version control, task runners, linters, and external APIs.
- Customization โ Tailor the editor to your specific needs without modifying the core application.
- Community โ Share your tools with the global Sublime Text community through Package Control.
Setting Up Your Development Environment
Before writing your first plugin, you need to set up a proper development environment. Sublime Text makes this straightforward since plugins are just Python files placed in specific directories.
Locating the Packages Directory
All plugins live in the Packages directory. You can find it by selecting Preferences > Browse Packages from the menu. On most systems, the path is:
- Windows:
%APPDATA%\Sublime Text\Packages - macOS:
~/Library/Application Support/Sublime Text/Packages - Linux:
~/.config/sublime-text/Packages
Enabling Debug Logging
During development, it is helpful to enable debug logging so you can see output from your plugin. Open the Sublime Text console with Ctrl+` (or Cmd+` on macOS) and enter:
sublime.log_commands(True)
sublime.log_input(True)
This will print every command and input event to the console, which is invaluable for debugging.
Writing Your First Plugin
Let us create a simple plugin that inserts a timestamp at the current cursor position. This is a classic beginner example that demonstrates the core concepts of plugin development.
Creating the Plugin File
Navigate to Tools > Developer > New Plugin in the menu. Sublime Text will generate a template file. Save it as timestamp.py inside a new folder called MyPlugins in your Packages directory.
import sublime
import sublime_plugin
from datetime import datetime
class InsertTimestampCommand(sublime_plugin.TextCommand):
def run(self, edit):
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
for region in self.view.sel():
self.view.insert(edit, region.begin(), timestamp)
Let us break down this code:
import sublimeโ Provides access to the core Sublime Text API.import sublime_pluginโ Contains base classes for commands and event listeners.InsertTimestampCommandโ The class name must end withCommand. Sublime Text converts CamelCase to snake_case, so this command becomesinsert_timestamp.sublime_plugin.TextCommandโ A base class for commands that operate on the current view (buffer).self.viewโ Represents the active file/buffer.self.view.sel()โ Returns the current selections in the view.editโ An edit object required for any modification to the buffer.
Running the Plugin
To run your new command, open the console and type:
view.run_command("insert_timestamp")
You should see a timestamp inserted at your cursor position. To make this more convenient, let us bind it to a keyboard shortcut.
Adding a Key Binding
Create a file named Default (Windows).sublime-keymap, Default (OSX).sublime-keymap, or Default (Linux).sublime-keymap inside your plugin folder. Add the following content:
[
{
"keys": ["ctrl+alt+t"],
"command": "insert_timestamp"
}
]
Now pressing Ctrl+Alt+T (or Cmd+Alt+T on macOS) will insert a timestamp.
Understanding Command Types
Sublime Text provides three primary command types, each suited for different use cases.
TextCommand
A TextCommand operates on the current view. It receives an edit object and has access to self.view. Use this when you need to read or modify the contents of a file.
class UpperCaseSelectionCommand(sublime_plugin.TextCommand):
def run(self, edit):
for region in self.view.sel():
if not region.empty():
selected_text = self.view.substr(region)
self.view.replace(edit, region, selected_text.upper())
WindowCommand
A WindowCommand operates on a window rather than a specific view. It has access to self.window and is useful for commands that open files, show panels, or manage layouts.
class OpenNewFileCommand(sublime_plugin.WindowCommand):
def run(self):
new_view = self.window.new_file()
new_view.set_name("Untitled.txt")
new_view.run_command("insert_snippet", {"contents": "// New file created by plugin\n"})
ApplicationCommand
An ApplicationCommand is the most general type. It does not have a built-in view or window attribute. Use it for global actions that are not tied to a specific context.
class QuitAllCommand(sublime_plugin.ApplicationCommand):
def run(self):
for window in sublime.windows():
window.run_command("close_window")
Working with the Sublime Text API
The Sublime Text API is the interface through which plugins interact with the editor. Understanding the key objects and methods is essential for building powerful plugins.
The sublime Module
The sublime module provides top-level functions and constants. Some commonly used functions include:
# Get the active window
window = sublime.active_window()
# Get settings
settings = sublime.load_settings("Preferences.sublime-settings")
font_size = settings.get("font_size", 10)
# Show a status message
sublime.status_message("Plugin loaded successfully")
# Display an error dialog
sublime.error_message("Something went wrong!")
# Set a timeout (runs once after delay in milliseconds)
sublime.set_timeout(lambda: print("Delayed execution"), 2000)
# Run on the main thread (useful for UI updates from worker threads)
sublime.set_timeout_async(lambda: print("Running async"), 0)
The View Object
The View object represents a single buffer. Here are some essential methods:
view = sublime.active_window().active_view()
# Get the file name
file_name = view.file_name()
# Get the entire buffer content
full_text = view.substr(sublime.Region(0, view.size()))
# Get text in a specific region
region = sublime.Region(0, 100)
text = view.substr(region)
# Find text using regex
regions = view.find_all(r"\bdef\s+\w+\b")
# Get the current cursor position
sel = view.sel()[0]
row, col = view.rowcol(sel.begin())
# Get the word at the cursor
word_region = view.word(sel.begin())
word = view.substr(word_region)
# Get the scope at a position
scope = view.scope_name(sel.begin())
# Insert, replace, and erase text (requires edit object)
view.insert(edit, 0, "// Header\n")
view.replace(edit, region, "new text")
view.erase(edit, region)
The Window Object
The Window object represents an editor window. Key methods include:
window = sublime.active_window()
# Open a file
window.open_file("/path/to/file.py")
# Show a quick panel (selection list)
items = ["Option 1", "Option 2", "Option 3"]
window.show_quick_panel(items, lambda index: print(f"Selected: {index}"))
# Show an input panel
window.show_input_panel(
"Enter your name:",
"default value",
lambda text: print(f"You entered: {text}"),
None,
None
)
# Get all open views
for view in window.views():
print(view.file_name())
# Show output panel
panel = window.create_output_panel("my_panel")
panel.run_command("append", {"characters": "Hello from plugin!"})
window.run_command("show_panel", {"panel": "output.my_panel"})
Event Listeners
Event listeners allow your plugin to respond automatically to editor events. By subclassing sublime_plugin.EventListener, you can hook into a wide variety of lifecycle events.
Common Event Hooks
class MyEventListener(sublime_plugin.EventListener):
def on_load(self, view):
"""Called when a file finishes loading."""
print(f"Loaded: {view.file_name()}")
def on_save(self, view):
"""Called after a file is saved."""
print(f"Saved: {view.file_name()}")
def on_modified(self, view):
"""Called when the view is modified."""
pass
def on_selection_modified(self, view):
"""Called when the selection changes."""
sel = view.sel()[0]
if not sel.empty():
word = view.substr(view.word(sel.begin()))
sublime.status_message(f"Selected word: {word}")
def on_activated(self, view):
"""Called when a view gains input focus."""
print(f"Activated: {view.name()}")
def on_close(self, view):
"""Called when a view is closed."""
print(f"Closed: {view.file_name()}")
def on_pre_save(self, view):
"""Called before a file is saved. You can modify the buffer here."""
pass
Practical Example: Auto-Strip Trailing Whitespace on Save
import sublime
import sublime_plugin
import re
class StripTrailingWhitespaceListener(sublime_plugin.EventListener):
def on_pre_save(self, view):
if not view.settings().get("auto_strip_whitespace", True):
return
edit = view.begin_edit("strip_whitespace")
try:
regions = view.find_all(r"[ \t]+$")
# Remove overlapping regions in reverse to preserve positions
for region in reversed(regions):
view.erase(edit, region)
finally:
view.end_edit(edit)
sublime.status_message("Stripped trailing whitespace")
Note that in Sublime Text 4, the begin_edit and end_edit pattern has been simplified. You can use TextCommand internally for buffer modifications, which is the recommended approach:
import sublime
import sublime_plugin
class StripTrailingWhitespaceListener(sublime_plugin.EventListener):
def on_pre_save(self, view):
if view.settings().get("auto_strip_whitespace", True):
view.run_command("strip_trailing_whitespace")
class StripTrailingWhitespaceCommand(sublime_plugin.TextCommand):
def run(self, edit):
regions = self.view.find_all(r"[ \t]+$")
for region in reversed(regions):
self.view.erase(edit, region)
Settings and Configuration
Plugins should be configurable so users can customize behavior without editing source code. Sublime Text uses JSON settings files for this purpose.
Creating Plugin Settings
Create a file named MyPlugin.sublime-settings in your plugin folder:
{
"timestamp_format": "%Y-%m-%d %H:%M:%S",
"insert_on_new_line": false,
"excluded_file_types": ["markdown", "plain text"]
}
Reading Settings in Your Plugin
import sublime
import sublime_plugin
from datetime import datetime
class InsertTimestampCommand(sublime_plugin.TextCommand):
def run(self, edit):
settings = sublime.load_settings("MyPlugin.sublime-settings")
fmt = settings.get("timestamp_format", "%Y-%m-%d %H:%M:%S")
timestamp = datetime.now().strftime(fmt)
excluded = settings.get("excluded_file_types", [])
syntax = self.view.settings().get("syntax", "")
for excluded_type in excluded:
if excluded_type in syntax:
sublime.status_message("Timestamp disabled for this file type")
return
for region in self.view.sel():
self.view.insert(edit, region.begin(), timestamp)
Listening for Settings Changes
If your plugin caches settings, you should listen for changes so users see updates immediately:
settings = sublime.load_settings("MyPlugin.sublime-settings")
settings.add_on_change("my_plugin_reload", lambda: print("Settings changed!"))
Adding Autocompletions
Providing autocompletions is one of the most useful features a plugin can offer. Sublime Text supports two approaches: completion files and programmatic completions.
Static Completions File
Create a .sublime-completions file in your plugin folder:
{
"scope": "source.python",
"completions": [
"def ",
"class ",
"import ",
"from ",
"if __name__ == \"__main__\":",
"print()",
"lambda "
]
}
Dynamic Completions with EventListener
For more sophisticated completions, use the on_query_completions event:
import sublime
import sublime_plugin
class MyCompletionsListener(sublime_plugin.EventListener):
COMPLETIONS = [
("function\tmy_plugin", "function ${1:name}(${2:args}):\n\t${0:pass}"),
("class\tmy_plugin", "class ${1:Name}:\n\t\"\"\"${2:docstring}\"\"\"\n\t${0:pass}"),
("logger\tmy_plugin", "logger = logging.getLogger(__name__)"),
]
def on_query_completions(self, view, prefix, locations):
# Only provide completions for Python files
if not view.match_selector(locations[0], "source.python"):
return []
return self.COMPLETIONS
Each completion is a tuple of (trigger, snippet). The trigger can include a label after a tab character for display in the autocomplete popup.
Creating Syntax Definitions
Syntax definitions tell Sublime Text how to tokenize and highlight files. In modern Sublime Text, syntax definitions are written in the .sublime-syntax YAML format.
Basic Syntax Definition Example
Create a file named MyLang.sublime-syntax:
%YAML 1.2
---
name: MyLang
file_extensions: [myl, mylang]
scope: source.mylang
contexts:
main:
- match: \b(if|else|while|for|return|function)\b
scope: keyword.control.mylang
- match: '"'
push: string
- match: '#.*$'
scope: comment.line.mylang
- match: \b\d+\b
scope: constant.numeric.mylang
- match: \b[A-Z][a-zA-Z0-9_]*\b
scope: entity.name.class.mylang
string:
- meta_scope: string.quoted.double.mylang
- match: '\\.'
scope: constant.character.escape.mylang
- match: '"'
pop: true
This definition handles keywords, strings, comments, numbers, and class names. The push and pop directives manage context stacks for nested constructs like strings.
Adding Menu Items and Command Palette Entries
To make your plugin discoverable, you should add entries to menus and the command palette.
Command Palette Entry
Create a file named Default.sublime-commands in your plugin folder:
[
{
"caption": "My Plugin: Insert Timestamp",
"command": "insert_timestamp"
},
{
"caption": "My Plugin: Strip Trailing Whitespace",
"command": "strip_trailing_whitespace"
}
]
Context Menu Entry
Create a file named Context.sublime-menu:
[
{
"caption": "My Plugin",
"children": [
{
"caption": "Insert Timestamp",
"command": "insert_timestamp"
},
{
"caption": "Uppercase Selection",
"command": "upper_case_selection"
}
]
}
]
Main Menu Entry
Create a file named Main.sublime-menu to add items to the top menu bar:
[
{
"caption": "My Plugin",
"mnemonic": "M",
"id": "my-plugin",
"children": [
{
"caption": "Insert Timestamp",
"command": "insert_timestamp"
},
{ "caption": "-" },
{
"caption": "Preferences",
"command": "edit_settings",
"args": {
"base_file": "${packages}/MyPlugin/MyPlugin.sublime-settings",
"default": "{\n\t// MyPlugin settings\n}\n"
}
}
]
}
]
Asynchronous Operations and Threading
Sublime Text runs plugins on the main thread by default. Long-running operations will freeze the UI, so you must use asynchronous execution for tasks like network requests or heavy computation.
Using set_timeout_async
import sublime
import sublime_plugin
import urllib.request
import json
class FetchApiDataCommand(sublime_plugin.TextCommand):
def run(self, edit):
sublime.status_message("Fetching data...")
sublime.set_timeout_async(self.fetch_data, 0)
def fetch_data(self):
try:
url = "https://api.github.com/repos/sublimehq/sublime_text"
with urllib.request.urlopen(url) as response:
data = json.loads(response.read().decode())
stars = data.get("stargazers_count", "unknown")
# Update UI on the main thread
sublime.set_timeout(
lambda: self.insert_result(stars), 0
)
except Exception as e:
sublime.set_timeout(
lambda: sublime.error_message(f"API Error: {e}"), 0
)
def insert_result(self, stars):
edit = self.view.begin_edit()
for region in self.view.sel():
self.view.insert(edit, region.begin(), f"Stars: {stars}")
self.view.end_edit(edit)
Using Threads Directly
import threading
class BackgroundWorker:
def __init__(self, view):
self.view = view
def start(self):
thread = threading.Thread(target=self.work, daemon=True)
thread.start()
def work(self):
# Perform heavy work here
result = self.expensive_computation()
# Schedule UI update on main thread
sublime.set_timeout(lambda: self.update_ui(result), 0)
def expensive_computation(self):
import time
time.sleep(3)
return "Done!"
def update_ui(self, result):
sublime.status_message(result)
Always remember: any operation that touches the Sublime Text API (views, windows, settings) must run on the main thread. Use sublime.set_timeout to schedule such operations from background threads.
Building a Complete Plugin Example
Let us build a more complete plugin called WordCounter that counts words in the current file and displays the result in the status bar. This example ties together commands, event listeners, and settings.
Directory Structure
WordCounter/
โโโ word_counter.py
โโโ WordCounter.sublime-settings
โโโ Default.sublime-commands
โโโ Context.sublime-menu
โโโ messages/
โโโ 1.0.0.txt
word_counter.py
import sublime
import sublime_plugin
def count_words(view):
"""Count words in the given view."""
content = view.substr(sublime.Region(0, view.size()))
words = content.split()
return len(words)
def count_chars(view):
"""Count characters in the given view."""
return view.size()
def count_lines(view):
"""Count lines in the given view."""
content = view.substr(sublime.Region(0, view.size()))
return content.count("\n") + 1
def update_status(view):
"""Update the status bar with word count."""
settings = sublime.load_settings("WordCounter.sublime-settings")
if not settings.get("enabled", True):
view.erase_status("word_counter")
return
words = count_words(view)
chars = count_chars(view)
lines = count_lines(view)
fmt = settings.get("status_format", "Words: {words} | Chars: {chars} | Lines: {lines}")
message = fmt.format(words=words, chars=chars, lines=lines)
view.set_status("word_counter", message)
class WordCounterListener(sublime_plugin.EventListener):
def on_modified(self, view):
update_status(view)
def on_load(self, view):
update_status(view)
def on_activated(self, view):
update_status(view)
def on_selection_modified(self, view):
settings = sublime.load_settings("WordCounter.sublime-settings")
if settings.get("show_selection_count", False):
sel = view.sel()[0]
if not sel.empty():
selected_text = view.substr(sel)
word_count = len(selected_text.split())
view.set_status("word_counter_sel", f"Selected: {word_count} words")
else:
view.erase_status("word_counter_sel")
class WordCountCommand(sublime_plugin.TextCommand):
def run(self, edit):
words = count_words(self.view)
chars = count_chars(self.view)
lines = count_lines(self.view)
message = f"Word Count: {words} words, {chars} characters, {lines} lines"
sublime.message_dialog(message)
class ToggleWordCounterCommand(sublime_plugin.ApplicationCommand):
def run(self):
settings = sublime.load_settings("WordCounter.sublime-settings")
current = settings.get("enabled", True)
settings.set("enabled", not current)
sublime.save_settings("WordCounter.sublime-settings")
status = "enabled" if not current else "disabled"
sublime.status_message(f"WordCounter {status}")
# Update all views
for window in sublime.windows():
for view in window.views():
if not current:
update_status(view)
else:
view.erase_status("word_counter")
WordCounter.sublime-settings
{
"enabled": true,
"show_selection_count": false,
"status_format": "Words: {words} | Chars: {chars} | Lines: {lines}"
}
Default.sublime-commands
[
{
"caption": "Word Counter: Show Count",
"command": "word_count"
},
{
"caption": "Word Counter: Toggle",
"command": "toggle_word_counter"
}
]
Packaging and Distribution
Once your plugin is complete, you will want to share it with the community. The standard distribution channel is Package Control, the de facto package manager for Sublime Text.
Creating a Package Metadata File
Create a package-metadata.json file in your plugin folder:
{
"name": "WordCounter",
"version": "1.0.0",
"description": "Real-time word, character, and line counting in the status bar.",
"author": "Your Name",
"homepage": "https://github.com/yourusername/WordCounter",
"issues": "https://github.com/yourusername/WordCounter/issues"
}
Adding a README
Include a README.md file with installation instructions, usage examples, configuration options, and screenshots. A well-documented README significantly increases adoption.
Submitting to Package Control
To get your plugin listed in Package Control:
- Host your plugin on a public Git repository (GitHub, GitLab, or Bitbucket).
- Tag a release with a semantic version number.
- Fork the
packagecontrol.iorepository. - Add your package to the
repositoryJSON file following the existing format. - Submit a pull request.
The entry in the repository file looks like this:
{
"name": "WordCounter",
"details": "https://github.com/yourusername/WordCounter",
"releases": [
{
"sublime_text": "*",
"details": "https://github.com/yourusername/WordCounter/tags"
}
],
"labels": ["text", "statistics", "productivity"]
}
Adding Release Messages
You can show users a message when they install or update your plugin. Create a messages folder with files named after version numbers (e.g., 1.0.0.txt) and add an entry in messages.json:
{
"install": "messages/install.txt",
"1.0.0": "messages/1.0.0.txt",
"1.1.0": "messages/1.1.0.txt"
}
Best Practices
1. Name Your Commands Carefully
Command names are global. Prefix them with your plugin name to avoid collisions with other plugins. For example, use word_counter_show instead of show.
2. Keep the UI Responsive
Never perform long-running operations on the main thread. Use sublime.set_timeout_async or Python threads for network requests, file I/O, and heavy computation. Always schedule UI updates back on the main thread with sublime.set_timeout.
3. Respect User Settings
Always provide sensible defaults and allow users to override behavior through settings. Check settings at runtime rather than caching them at module load time, unless you also listen for changes.
4. Scope Your Event Listeners
Event listeners fire for every view. Always check the file type or scope before performing work to avoid unnecessary processing:
def on_modified(self, view):
if not view.match_selector(0, "source.python"):
return
# Proceed with Python-specific logic
5. Handle Errors Gracefully
Wrap risky operations in try/except blocks and provide meaningful feedback to the user. Unhandled exceptions in plugins can destabilize the editor.
try:
result = perform_risky_operation()
except Exception as e:
sublime.error_message(f"MyPlugin encountered an error:\n\n{e}")
return
6. Use Semantic Versioning
Follow semantic versioning (MAJOR.MINOR.PATCH) for your releases. This helps users understand the impact of updates and allows Package Control to handle upgrades correctly.
7. Write Clean, Documented Code
Since plugins are open source, write code that others can read and contribute to. Add docstrings, comments, and type hints where appropriate. Organize your code into logical modules if the plugin grows large.
8. Test Across Platforms
Sublime Text runs on Windows, macOS, and Linux. Test your plugin on all three platforms, especially if you use platform-specific features like file paths or external commands.
9. Avoid Global State
Minimize the use of module-level variables. Global state can lead to subtle bugs, especially when multiple windows or views are involved. Use settings or per-view storage instead.
10. Clean Up After Yourself
If your plugin registers callbacks, timers, or creates temporary files, make sure to clean them up when the plugin is unloaded or when views are closed. Use plugin_unloaded for cleanup:
def plugin_unloaded():
# Clean up resources
settings = sublime.load_settings("MyPlugin.sublime-settings")
settings.clear_on_change("my_plugin_reload")
sublime.status_message("MyPlugin unloaded")
Debugging Techniques
Using the Console
The Sublime Text console (accessible via Ctrl+`) is your primary debugging tool. Use print() statements liberally during development. You can also interact with the API directly in the console:
# Inspect the active view
view = sublime.active_window().active_view()
print(view.file_name())
print(view.settings().get("syntax"))
# Run a command
view.run_command("insert_timestamp")
# Inspect all loaded packages
print(sublime.packages_path())
Logging to a File
For complex plugins, consider logging to a file for persistent debugging:
import logging
import os
log_path = os.path.join(sublime.packages_path(), "MyPlugin", "debug.log")
logging.basicConfig(
filename=log_path,
level=logging.DEBUG,
format="%(asctime)s [%(levelname)s] %(message)s"
)
logger = logging.getLogger("MyPlugin")
# Usage
logger.info("Plugin started")
logger.debug(f"View size: {view.size()}")
Handling Reloads During Development
Sublime Text automatically reloads Python files when you save them, but sometimes you need a full reload. You can use the PackageDev package or manually reload in the console:
import importlib
import MyPlugin.word_counter
importlib.reload(MyPlugin.word_counter)
Advanced Topics
Phantom and HTML Popups
Sublime Text 4 supports phantoms (inline HTML content) and popups, which allow you to create rich UI elements within the editor:
class ShowPhantomCommand(sublime_plugin.TextCommand):
def run(self, edit):
view = self.view
sel = view.sel()[0]
word = view.substr(view.word(sel.begin()))
content = f'''
Word: {word}
Length: {len(word)} characters
Working with Multiple Cursors
Sublime Text is famous for its multiple cursor support. Your plugins should handle multiple selections gracefully:
class WrapSelectionCommand(sublime_plugin.TextCommand):
def run(self, edit):
wrapper = self.view.settings().get("wrap_character", '"')
for region in self.view.sel():
if not region.empty():
text = self.view.substr(region)
wrapped = f"{wrapper}{text}{wrapper}"
self.view.replace(edit, region, wrapped)
Creating Build Systems
You can bundle build systems with your plugin. Create a .sublime-build file:
{
"selector": "source.mylang",
"cmd": ["mylang", "$file"],
"file_regex": "^(.+):(\\d+):(\\d+):\\s+(.+)$",
"working_dir": "$file_path",
"variants": [
{
"name": "Run",
"cmd": ["mylang", "--run", "$file"]
},
{
"name": "Check Syntax",
"cmd": ["mylang", "--check", "$file"]
}
]
}
Conclusion
Sublime Text plugins offer a powerful way to extend and personalize one of the fastest and most beloved code editors available. By leveraging Python and the comprehensive Sublime Text API, you can build everything from simple text manipulation commands to full-featured language support tools. The key to successful plugin development lies in understanding the command system, mastering the API objects (View, Window, and the sublime module), respecting the main-thread constraint for UI operations, and following community best practices for