Hooks
Hooks let extensions intercept command execution. Register before-hooks to modify arguments or cancel commands, and after-hooks to react to command results.
Before hooks
A before hook runs before the command handler. If the hook returns false, the command is cancelled and subsequent before hooks and the command itself are skipped.
editor.commands.registerHook(
{
commandId: "project.run",
type: "before",
priority: 50
},
async (args) => {
const activeFile = await editor.workspace.getActiveFile();
if (activeFile?.name.endsWith(".py")) {
// Handle Python files ourselves
await editor.terminal.open({
name: `Python: ${activeFile.name}`,
command: `python "${activeFile.name}"`,
});
return false; // Cancel the default "Run" behavior
}
return true; // Allow default behavior for other files
}
);
Return values for before hooks:
| Return value | Effect |
|---|---|
true (or any truthy value) | Allow the command to proceed. |
false | Cancel the command. No further before hooks run, and the command handler is not called. |
| Promise rejection / thrown error | The error is logged, and the command proceeds (fail-open). A broken hook does not block the command. |
After hooks
An after hook runs after the command handler completes. It receives the command's return value as its argument. After hooks are fire-and-forget: their return values are ignored, and errors are logged but do not affect the caller.
editor.commands.registerHook(
{
commandId: "editor.save",
type: "after",
priority: 50
},
async (result) => {
// Log every save
console.log("File saved:", result);
// Auto-format after save
const file = await editor.workspace.getActiveFile();
if (file?.name.endsWith(".js")) {
await editor.commands.execute("editor.format");
}
}
);
Hook priority
Hooks are sorted by priority in ascending order -- lower numbers run first.
| Priority | Description |
|---|---|
| 1-49 | High priority. Runs before most other hooks. |
| 50-99 | Normal priority. Good default for most extensions. |
| 100 | Default priority if not specified. |
| 101+ | Low priority. Runs after other hooks. |
When multiple extensions register hooks on the same command, priority determines execution order. If two hooks have the same priority, they run in registration order.
Hook timeout
Before hooks have a 30-second timeout. If an extension does not respond with true or false within that window, the hook defaults to true (allow), and the command proceeds. This prevents a stalled extension from blocking the editor indefinitely.
After hooks have no timeout constraint since they are fire-and-forget.
registerHook signature
const { hookId } = await editor.commands.registerHook(options, handler);
| Parameter | Type | Description |
|---|---|---|
options.commandId | string | The command to hook. Must already be registered. |
options.type | 'before' | 'after' | When to run the hook. |
options.priority | number | Execution priority. Default: 100. |
handler | function | For before hooks: (args) => boolean | Promise<boolean> — return false to cancel. For after hooks: (result) => void — receives the command result; return value is ignored. |
Returns: A Promise<{ hookId: string }> with the assigned hook ID (useful for debugging).