ワンクリックで
vs-code-extension-patterns
Expert knowledge of VS Code Extension API patterns, TreeDataProvider implementation, and command registration
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Expert knowledge of VS Code Extension API patterns, TreeDataProvider implementation, and command registration
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
| name | VS Code Extension Patterns |
| description | Expert knowledge of VS Code Extension API patterns, TreeDataProvider implementation, and command registration |
| version | 1.0.0 |
This skill provides deep expertise in VS Code Extension API patterns, common implementations, and best practices for building robust VS Code extensions.
Expert knowledge of implementing tree views in VS Code extensions:
export class MyTreeProvider implements vscode.TreeDataProvider<TreeItem> {
// Event emitter for refresh notifications
private _onDidChangeTreeData = new vscode.EventEmitter<TreeItem | undefined>();
readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
// Trigger refresh
refresh(): void {
this._onDidChangeTreeData.fire(undefined);
}
// Convert data to tree item
getTreeItem(element: TreeItem): vscode.TreeItem {
const item = new vscode.TreeItem(
element.label,
element.children ? vscode.TreeItemCollapsibleState.Collapsed : vscode.TreeItemCollapsibleState.None
);
// Set icon using ThemeIcon for consistency
item.iconPath = new vscode.ThemeIcon('file');
// Add command for click action
item.command = {
command: 'extension.openItem',
title: 'Open',
arguments: [element]
};
// Set context value for context menu filtering
item.contextValue = element.type;
return item;
}
// Get children for a node
async getChildren(element?: TreeItem): Promise<TreeItem[]> {
if (!element) {
return this.getRootItems();
}
return element.children || [];
}
}
Key Patterns:
EventEmitter for change notificationsgetTreeItem() and getChildren() methodscontextValue for context menu filteringThemeIcon for built-in iconsgetChildren()Proper command registration patterns:
export function activate(context: vscode.ExtensionContext) {
// Register command
const disposable = vscode.commands.registerCommand(
'extension.myCommand',
async (...args) => {
try {
// Command implementation
vscode.window.showInformationMessage('Command executed!');
} catch (error) {
vscode.window.showErrorMessage(
`Command failed: ${error instanceof Error ? error.message : String(error)}`
);
}
}
);
// CRITICAL: Add to subscriptions for disposal
context.subscriptions.push(disposable);
}
Best Practices:
context.subscriptionsUnderstanding extension activation and deactivation:
// Activation
export async function activate(context: vscode.ExtensionContext) {
console.log('Extension activating...');
// Register providers
const treeProvider = new MyTreeProvider();
vscode.window.registerTreeDataProvider('myView', treeProvider);
// Register commands
context.subscriptions.push(
vscode.commands.registerCommand('extension.refresh', () => {
treeProvider.refresh();
})
);
// Set up file watchers
const watcher = vscode.workspace.createFileSystemWatcher('**/*.config');
watcher.onDidChange(() => treeProvider.refresh());
context.subscriptions.push(watcher);
console.log('Extension activated successfully');
}
// Deactivation (cleanup)
export function deactivate() {
console.log('Extension deactivating...');
// Cleanup if needed (disposables auto-disposed)
}
Activation Events:
onStartupFinished - Activate after VS Code loads (recommended)* - Activate immediately (use sparingly)onCommand: - Activate when command executedonView: - Activate when view openedonLanguage: - Activate for specific file typesWorking with VS Code settings:
// Read configuration
const config = vscode.workspace.getConfiguration('myExtension');
const setting = config.get<string>('mySetting', 'defaultValue');
// Write configuration
await config.update('mySetting', newValue, vscode.ConfigurationTarget.Global);
// Listen for configuration changes
context.subscriptions.push(
vscode.workspace.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('myExtension.mySetting')) {
// Reload or update based on new setting
treeProvider.refresh();
}
})
);
Configuration Targets:
Global - User settingsWorkspace - Workspace settingsWorkspaceFolder - Folder-specific settingsMonitoring file changes:
// Watch specific pattern
const watcher = vscode.workspace.createFileSystemWatcher(
new vscode.RelativePattern(workspaceFolder, '.claude/**/*')
);
// Handle events
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();
});
// CRITICAL: Dispose watcher
context.subscriptions.push(watcher);
Showing messages and prompts:
// Information message
vscode.window.showInformationMessage('Operation successful!');
// Warning message
vscode.window.showWarningMessage('This action cannot be undone');
// Error message
vscode.window.showErrorMessage('Operation failed');
// Confirmation dialog
const answer = await vscode.window.showWarningMessage(
'Delete this file?',
{ modal: true },
'Delete',
'Cancel'
);
if (answer === 'Delete') {
// Proceed with deletion
}
// Input box
const input = await vscode.window.showInputBox({
prompt: 'Enter name',
placeHolder: 'my-file.ts',
validateInput: (value) => {
return value.length === 0 ? 'Name cannot be empty' : undefined;
}
});
// Quick pick
const selection = await vscode.window.showQuickPick(
['Option 1', 'Option 2', 'Option 3'],
{ placeHolder: 'Select an option' }
);
Creating custom UI panels:
const panel = vscode.window.createWebviewPanel(
'myView',
'My View',
vscode.ViewColumn.One,
{
enableScripts: true,
retainContextWhenHidden: true
}
);
// Set HTML content
panel.webview.html = getWebviewContent();
// Handle messages from webview
panel.webview.onDidReceiveMessage(
message => {
switch (message.command) {
case 'save':
// Handle save
break;
}
},
undefined,
context.subscriptions
);
// Dispose
context.subscriptions.push(panel);
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 viewviewItem == type - Filter by tree item context valueviewItem =~ /pattern/ - Regex matching!expression - Negationa && b - Logical ANDa || b - Logical ORCreating status bar indicators:
const statusBarItem = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Left,
100 // Priority
);
statusBarItem.text = '$(check) Ready';
statusBarItem.tooltip = 'Extension status';
statusBarItem.command = 'extension.showStatus';
statusBarItem.show();
context.subscriptions.push(statusBarItem);
// Update dynamically
function updateStatus(text: string) {
statusBarItem.text = `$(sync~spin) ${text}`;
}
Creating output channels for logging:
const outputChannel = vscode.window.createOutputChannel('My Extension');
outputChannel.appendLine('Extension started');
outputChannel.appendLine(`Loaded ${items.length} items`);
// Show output panel
outputChannel.show();
// Dispose
context.subscriptions.push(outputChannel);
// Bad - memory leak
const watcher = vscode.workspace.createFileSystemWatcher('**/*');
// Good - properly disposed
const watcher = vscode.workspace.createFileSystemWatcher('**/*');
context.subscriptions.push(watcher);
// Bad - synchronous I/O
const content = fs.readFileSync(path, 'utf-8');
// Good - asynchronous
const content = await fs.promises.readFile(path, 'utf-8');
// esbuild.mjs
export default {
external: ['vscode'], // CRITICAL: Never bundle vscode
};
// Bad
treeProvider.refresh();
// Good
try {
await loadData();
treeProvider.refresh();
} catch (error) {
vscode.window.showErrorMessage(`Failed to load data: ${error.message}`);
}
// Bad
const configPath = '/Users/name/.claude/config.json';
// Good
import * as path from 'path';
const homeDir = process.env.HOME || process.env.USERPROFILE;
const configPath = path.join(homeDir, '.claude', 'config.json');
When working on VS Code extension code, this skill provides: