| name | VS Code Extension Patterns |
| description | Expert knowledge of VS Code Extension API patterns, TreeDataProvider implementation, and command registration |
| version | 1.0.0 |
VS Code Extension Patterns Skill
This skill provides deep expertise in VS Code Extension API patterns, common implementations, and best practices for building robust VS Code extensions.
Core Competencies
1. TreeDataProvider Implementation
Expert knowledge of implementing tree views in VS Code extensions:
export class MyTreeProvider implements vscode.TreeDataProvider<TreeItem> {
private _onDidChangeTreeData = new vscode.EventEmitter<TreeItem | undefined>();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
refresh(): void {
this._onDidChangeTreeData.fire(undefined);
}
getTreeItem(element: TreeItem): vscode.TreeItem {
const item = new vscode.TreeItem(
element.label,
element.children ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None
);
item.iconPath = new vscode.ThemeIcon('file');
item.command = {
command: 'extension.openItem',
title: 'Open',
arguments: [element]
};
item.contextValue = element.type;
return item;
}
async getChildren(element?: TreeItem): Promise<TreeItem[]> {
if (!element) {
return this.getRootItems();
}
return element.children || [];
}
}
Key Patterns:
- Use
EventEmitter for change notifications
- Implement
getTreeItem() and getChildren() methods
- Set
contextValue for context menu filtering
- Use
ThemeIcon for built-in icons
- Attach commands to tree items for click actions
- Handle async data loading in
getChildren()
2. Command Registration
Proper command registration patterns:
export function activate(context: vscode.ExtensionContext) {
const disposable = vscode.commands.registerCommand(
'extension.myCommand',
async (...args) => {
try {
vscode.window.showInformationMessage('Command executed!');
} catch (error) {
vscode.window.showErrorMessage(
`Command failed: ${error instanceof Error ? error.message : String(error)}`
);
}
}
);
context.subscriptions.push(disposable);
}
Best Practices:
- Always add disposables to
context.subscriptions
- Handle errors and show user-friendly messages
- Use async/await for asynchronous operations
- Validate arguments before processing
- Provide meaningful error messages
3. Extension Lifecycle
Understanding extension activation and deactivation:
export async function activate(context: vscode.ExtensionContext) {
console.log('Extension activating...');
const treeProvider = new MyTreeProvider();
vscode.window.registerTreeDataProvider('myView', treeProvider);
context.subscriptions.push(
vscode.commands.registerCommand('extension.refresh', () => {
treeProvider.refresh();
})
);
const watcher = vscode.workspace.createFileSystemWatcher('**/*.config');
watcher.onDidChange(() => treeProvider.refresh());
context.subscriptions.push(watcher);
console.log('Extension activated successfully');
}
export function deactivate() {
console.log('Extension deactivating...');
}
Activation Events:
onStartupFinished - Activate after VS Code loads (recommended)
* - Activate immediately (use sparingly)
onCommand: - Activate when command executed
onView: - Activate when view opened
onLanguage: - Activate for specific file types
4. Configuration Management
Working with VS Code settings:
const config = vscode.workspace.getConfiguration('myExtension');
const setting = config.get<string>('mySetting', 'defaultValue');
await config.update('mySetting', newValue, vscode.ConfigurationTarget.Global);
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('myExtension.mySetting')) {
treeProvider.refresh();
}
})
);
Configuration Targets:
Global - User settings
Workspace - Workspace settings
WorkspaceFolder - Folder-specific settings
5. File System Watchers
Monitoring file changes:
const watcher = vscode.workspace.createFileSystemWatcher(
new vscode.RelativePattern(workspaceFolder, '.claude/**/*')
);
watcher.onDidCreate(uri => {
console.log('File created:', uri.fsPath);
treeProvider.refresh();
});
watcher.onDidChange(uri => {
console.log('File changed:', uri.fsPath);
treeProvider.refresh();
});
watcher.onDidDelete(uri => {
console.log('File deleted:', uri.fsPath);
treeProvider.refresh();
});
context.subscriptions.push(watcher);
6. User Interaction
Showing messages and prompts:
vscode.window.showInformationMessage('Operation successful!');
vscode.window.showWarningMessage('This action cannot be undone');
vscode.window.showErrorMessage('Operation failed');
const answer = await vscode.window.showWarningMessage(
'Delete this file?',
{ modal: true },
'Delete',
'Cancel'
);
if (answer === 'Delete') {
}
const input = await vscode.window.showInputBox({
prompt: 'Enter name',
placeHolder: 'my-file.ts',
validateInput: (value) => {
return value.length === 0 ? 'Name cannot be empty' : undefined;
}
});
const selection = await vscode.window.showQuickPick(
['Option 1', 'Option 2', 'Option 3'],
{ placeHolder: 'Select an option' }
);
7. WebView Panels
Creating custom UI panels:
const panel = vscode.window.createWebviewPanel(
'myView',
'My View',
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true
}
);
panel.webview.html = getWebviewContent();
panel.webview.onDidReceiveMessage(
message => {
switch (message.command) {
case 'save':
break;
}
},
undefined,
context.subscriptions
);
context.subscriptions.push(panel);
8. Context Menus
Adding context menu items:
In package.json:
{
"contributes": {
"menus": {
"view/item/context": [
{
"command": "extension.editItem",
"when": "view == myView && viewItem == editableItem",
"group": "inline"
}
],
"view/title": [
{
"command": "extension.refresh",
"when": "view == myView",
"group": "navigation"
}
]
}
}
}
When Clauses:
view == myView - Show only in specific view
viewItem == type - Filter by tree item context value
viewItem =~ /pattern/ - Regex matching
!expression - Negation
a && b - Logical AND
a || b - Logical OR
9. Status Bar Items
Creating status bar indicators:
const statusBarItem = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Left,
100
);
statusBarItem.text = '$(check) Ready';
statusBarItem.tooltip = 'Extension status';
statusBarItem.command = 'extension.showStatus';
statusBarItem.show();
context.subscriptions.push(statusBarItem);
function updateStatus(text: string) {
statusBarItem.text = `$(sync~spin) ${text}`;
}
10. Output Channels
Creating output channels for logging:
const outputChannel = vscode.window.createOutputChannel('My Extension');
outputChannel.appendLine('Extension started');
outputChannel.appendLine(`Loaded ${items.length} items`);
outputChannel.show();
context.subscriptions.push(outputChannel);
Common Pitfalls to Avoid
1. Not Disposing Resources
const watcher = vscode.workspace.createFileSystemWatcher('**/*');
const watcher = vscode.workspace.createFileSystemWatcher('**/*');
context.subscriptions.push(watcher);
2. Blocking the Extension Host
const content = fs.readFileSync(path, 'utf-8');
const content = await fs.promises.readFile(path, 'utf-8');
3. Bundling vscode Module
export default {
external: ['vscode'],
};
4. Ignoring Errors
treeProvider.refresh();
try {
await loadData();
treeProvider.refresh();
} catch (error) {
vscode.window.showErrorMessage(`Failed to load data: ${error.message}`);
}
5. Hardcoding Paths
const configPath = '/Users/name/.claude/config.json';
import * as path from 'path';
const homeDir = process.env.HOME || process.env.USERPROFILE;
const configPath = path.join(homeDir, '.claude', 'config.json');
Testing in Extension Development Host
- Press F5 to launch Extension Development Host
- New VS Code window opens with extension loaded
- Set breakpoints in TypeScript code
- Debug Console shows logs and errors
- Restart with Cmd+Shift+F5 after code changes
Resources
Usage
When working on VS Code extension code, this skill provides:
- Correct implementation patterns
- Best practices and conventions
- Common pitfall avoidance
- Proper error handling
- Resource disposal patterns
- Testing strategies