A guided tour of the vscode namespace — the objects, events and disposables you use to read, edit and react to the editor
This part assumes you have an activated extension from Parts 1–2. We now go deep on the runtime API surface — the vscode module your code imports.
vscode namespace mapcontext.subscriptionswindow namespaceworkspace & editorsfindFiles, configurationTextDocument & TextEditoredit() & WorkspaceEditworkspace.fs & file-system watchersvscode Namespace MapYour extension imports a single module — import * as vscode from 'vscode'; — whose top-level namespaces group every API by concern.
This deck focuses on window, workspace and the editor/document/file types they expose — the spine of almost every extension.
context.subscriptionsAlmost everything you register returns a vscode.Disposable. Push each one onto context.subscriptions so VS Code disposes it automatically on deactivate.
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
// registerCommand returns a Disposable
const cmd = vscode.commands.registerCommand(
'myExt.hello', () => vscode.window.showInformationMessage('Hi!')
);
// Event subscriptions are Disposables too
const sub = vscode.workspace.onDidSaveTextDocument(doc =>
console.log('saved', doc.fileName)
);
// Hand ownership to VS Code — cleaned up on deactivate
context.subscriptions.push(cmd, sub);
// Manual disposal is also possible:
// const d = vscode.Disposable.from(cmd, sub); d.dispose();
}
export function deactivate() {}
Disposable.push() it to subscriptions.dispose() for youIf a call returns a Disposable, it belongs in context.subscriptions.
window — User MessagesThree notification levels, each returning a promise. Pass string arguments to add buttons; the promise resolves to the clicked label (or undefined).
// Plain notifications
vscode.window.showInformationMessage('Build complete.');
vscode.window.showWarningMessage('Unsaved changes.');
vscode.window.showErrorMessage('Build failed.');
// Buttons → resolves to the chosen string
const choice = await vscode.window.showInformationMessage(
'Delete this file?',
'Delete', 'Cancel'
);
if (choice === 'Delete') { /* ... */ }
// Modal blocks until answered
const ok = await vscode.window.showWarningMessage(
'This cannot be undone.',
{ modal: true },
'Proceed'
);
// MessageItem objects let you attach metadata
const picked = await vscode.window.showErrorMessage(
'Port in use.',
{ title: 'Retry' }, { title: 'Ignore', isCloseAffordance: true }
);
showInformationMessageshowWarningMessageshowErrorMessage{ modal: true } forces a responseMessageItem — title, isCloseAffordanceAlways await the result — the promise is how you learn which button was pressed.
window — Quick PickThe Quick Pick is VS Code's searchable dropdown. Use showQuickPick for the simple case, or createQuickPick when you need full control.
showQuickPick// From an array of strings
const fruit = await vscode.window.showQuickPick(
['Apple', 'Banana', 'Cherry'],
{ placeHolder: 'Pick a fruit' }
);
// From rich QuickPickItems
const items: vscode.QuickPickItem[] = [
{ label: '$(file) Open File', description: 'Ctrl+O',
detail: 'Open a file from disk' },
{ label: '$(search) Find', description: 'Ctrl+F' },
];
const chosen = await vscode.window.showQuickPick(items, {
matchOnDescription: true, canPickMany: false,
});
chosen?.label; // selected item
createQuickPickconst qp = vscode.window.createQuickPick();
qp.placeholder = 'Type to filter…';
qp.items = [
{ label: 'main.ts', description: 'src/' },
{ label: 'utils.ts', description: 'src/lib/' },
];
qp.onDidChangeValue(v => { /* live filter / fetch */ });
qp.onDidAccept(() => {
const sel = qp.selectedItems[0];
vscode.window.showInformationMessage(sel.label);
qp.hide();
});
qp.onDidHide(() => qp.dispose());
qp.show();
QuickPickItemlabel (with $(codicon)) · description (greyed, inline) · detail (second line).
window — Input BoxPrompt the user for free text with showInputBox. validateInput runs on every keystroke — return a string to show an error, or undefined / null to accept.
const name = await vscode.window.showInputBox({
prompt: 'Name your new component',
placeHolder: 'e.g. UserCard',
value: 'MyComponent', // pre-filled default
ignoreFocusOut: true, // survive focus loss
validateInput: (text) => {
if (!text.trim()) return 'Name cannot be empty';
if (!/^[A-Z]/.test(text))
return 'Components must start with a capital letter';
return undefined; // valid
},
});
if (name === undefined) {
return; // user pressed Escape
}
vscode.window.showInformationMessage(`Creating ${name}…`);
InputBoxOptionsprompt — the label textplaceHolder — ghost hintvalue — initial contentspassword — masks inputignoreFocusOut — keep openvalidateInputstring → error messageundefined → validasync (returns a promise)Resolves to undefined — always check before proceeding.
window — Status BarCreate a persistent item with createStatusBarItem(alignment, priority). Set .text (with $(codicon) icons), wire a .command, then call .show().
const item = vscode.window.createStatusBarItem(
vscode.StatusBarAlignment.Right, // Left | Right
100 // priority (higher = further left)
);
item.text = '$(rocket) Deploy'; // codicon + label
item.tooltip = 'Deploy to staging';
item.command = 'myExt.deploy'; // click → runs command
item.backgroundColor =
new vscode.ThemeColor('statusBarItem.warningBackground');
item.show();
context.subscriptions.push(item); // dispose on deactivate
// Update it later, e.g. from an event
item.text = '$(sync~spin) Deploying…';
StatusBarAlignment.Left / .Right.text — supports $(icon) codicons.tooltip, .command.color / .backgroundColor via ThemeColor.show() / .hide()Animate with ~spin, e.g. $(sync~spin). Browse the full set in the codicon reference.
window — Progress & Long TasksWrap any async work in withProgress. VS Code shows a spinner or bar and resolves when your callback's promise settles. Report incremental updates via progress.report.
await vscode.window.withProgress(
{
location: vscode.ProgressLocation.Notification,
title: 'Indexing project',
cancellable: true,
},
async (progress, token) => {
const files = await vscode.workspace.findFiles('**/*.ts');
const step = 100 / files.length;
for (const [i, file] of files.entries()) {
if (token.isCancellationRequested) break;
progress.report({
increment: step, // adds to the bar (0–100)
message: `${i + 1}/${files.length} ${file.path}`,
});
await indexFile(file);
}
}
);
ProgressLocation.Notification — toast with bar.Window — spinner in status bar.SourceControl — in the SCM viewprogress.reportincrement — percentage to addmessage — live status lineincrement for indeterminateSet cancellable: true, then poll token.isCancellationRequested in your loop.
workspace — Folders & FilesThe workspace namespace describes the folders the user has open and lets you find, open and configure files across them.
// Open folders (undefined if none)
const folders = vscode.workspace.workspaceFolders;
const root = folders?.[0].uri; // a vscode.Uri
// Glob for files (respects .gitignore by default)
const tsFiles = await vscode.workspace.findFiles(
'**/*.ts', // include glob
'**/node_modules/**', // exclude glob
100 // max results
);
// Open a document (does not show it in an editor)
const doc = await vscode.workspace.openTextDocument(tsFiles[0]);
const docFromString = await vscode.workspace.openTextDocument({
language: 'json', content: '{ "ok": true }',
});
// Read settings (extension + user + workspace merged)
const cfg = vscode.workspace.getConfiguration('myExt');
const max = cfg.get('maxItems', 50); // with default
await cfg.update('maxItems', 100,
vscode.ConfigurationTarget.Workspace);
workspaceFolders — array or undefineduri, name, indexgetWorkspaceFolder(uri) resolves ownerfindFiles(include, exclude, max)openTextDocument(uri | options)window.showTextDocument(doc)getConfiguration('section').get / .update / .hasonDidChangeConfigurationTextDocument & TextEditorA TextDocument is the file's content model; a TextEditor is a view onto it, carrying the selection. Positions are zero-based.
const editor = vscode.window.activeTextEditor;
if (!editor) return; // no editor focused
const doc = editor.document;
const fullText = doc.getText(); // entire file as string
const lineCount = doc.lineCount;
const langId = doc.languageId; // 'typescript', etc.
// The current selection (a Range of two Positions)
const sel: vscode.Selection = editor.selection;
const selectedText = doc.getText(sel);
const cursor: vscode.Position = sel.active; // line, character
// Build positions and ranges explicitly
const start = new vscode.Position(0, 0);
const end = new vscode.Position(2, 10);
const range = new vscode.Range(start, end);
const firstThreeLines = doc.getText(range);
// Inspect a single line
const line = doc.lineAt(cursor.line);
line.text; // the line's string
line.isEmptyOrWhitespace; // boolean
line.range; // a Range covering it
TextDocumentgetText(range?), lineAt(n)lineCount, languageId, uriisDirty, save()offsetAt / positionAtTextEditordocument, selection, selectionsvisibleRanges, revealRange()edit(...) — mutate the bufferPosition / RangeBoth are immutable. A Selection is a Range with anchor and active ends.
For the active editor, use editor.edit(). For changes spanning multiple files (or applied without focus), build a WorkspaceEdit and apply it.
editor.editconst editor = vscode.window.activeTextEditor!;
await editor.edit(editBuilder => {
// Insert at a Position
editBuilder.insert(
new vscode.Position(0, 0), '// header\n'
);
// Replace a Range
editBuilder.replace(editor.selection, 'REPLACED');
// Delete a Range
editBuilder.delete(
new vscode.Range(5, 0, 6, 0)
);
});
// All edits in one builder = one undo step
Operates on the visible, active editor only. Atomic & undoable as a unit.
WorkspaceEditconst edit = new vscode.WorkspaceEdit();
edit.insert(uriA, new vscode.Position(0, 0), '// A\n');
edit.replace(uriB,
new vscode.Range(1, 0, 1, 5), 'hello');
edit.delete(uriC, new vscode.Range(0, 0, 1, 0));
// Can also create / rename / delete whole files
edit.createFile(newUri, { ignoreIfExists: true });
edit.renameFile(oldUri, newUri);
const applied = await vscode.workspace.applyEdit(edit);
Spans many files, no editor needed. Powers refactors, rename providers and code actions.
Use workspace.fs for raw file I/O (works on remote/virtual file systems), and the event APIs to react as documents change.
const fs = vscode.workspace.fs;
// Read → returns a Uint8Array
const bytes = await fs.readFile(uri);
const text = new TextDecoder().decode(bytes);
// Write ← expects a Uint8Array
await fs.writeFile(uri,
new TextEncoder().encode('hello\n'));
// Metadata, dirs, copy, delete
const info = await fs.stat(uri); // FileStat
await fs.createDirectory(dirUri);
await fs.delete(uri, { recursive: true });
// Watch a glob for create/change/delete
const watcher =
vscode.workspace.createFileSystemWatcher('**/*.ts');
watcher.onDidCreate(uri => { /* … */ });
watcher.onDidChange(uri => { /* … */ });
watcher.onDidDelete(uri => { /* … */ });
// Document lifecycle events
vscode.workspace.onDidSaveTextDocument(doc => {});
vscode.workspace.onDidChangeTextDocument(e => {
e.document; e.contentChanges; // what changed
});
// Remember: push every watcher/listener to subscriptions
workspace.fsreadFile / writeFile — Uint8Arraystat, readDirectory, copy, deleteTextEncoder/TextDecodercreateFileSystemWatcher(glob)onDidSaveTextDocumentonDidChangeTextDocumentDisposablescontext.globalState — across windowscontext.workspaceState — per workspacecontext.secrets — encrypted storecontext.extensionUri — bundled assetsvscode module groups APIs into namespaces — window, workspace, commands, languages…Disposable → push to context.subscriptionswindow drives all UI: messages, Quick Pick, Input Box, status bar, progressworkspace finds files, reads config, and exposes fs + eventseditor.edit() (one file) or WorkspaceEdit (many)Build a command that:
findFiles → feed results into a Quick PickopenTextDocument + showTextDocument the chosen fileeditor.edit() to insert a timestamped header at the topDisposable to subscriptions$(icon) namesPart 4 — Webviews & Language Features: custom UI panels, completion, hovers, diagnostics and code actions.