RPDI
Back to Blog

How to Track Active Files in a VS Code Extension: The Complete API Guide

TL;DR

Tracking active files in a VS Code extension requires choosing between three APIs: window.activeTextEditor (current focused file), window.tabGroups (all open tabs), and workspace.textDocuments (all loaded documents). Each API serves a different use case and fires different events. This tutorial covers all three with production TypeScript implementations.

The Three File Tracking APIs

VS Code exposes file state through three distinct APIs. Understanding which to use — and when — is the difference between a reliable context system and a fragile one:

Analysis

window.activeTextEditor

Returns the currently focused editor. Updates when the developer switches tabs. Use this for: 'what file is the developer looking at RIGHT NOW?' Event: window.onDidChangeActiveTextEditor. Limitation: only tracks one file at a time.

Analysis

window.tabGroups.all

Returns all open tabs across all tab groups. Includes: file URI, active state, pinned state. Use this for: 'what files does the developer consider part of their working set?' Event: window.tabGroups.onDidChangeTabs. Available since VS Code 1.67.

Analysis

workspace.textDocuments

Returns all documents loaded in memory. Includes files open in editors, files loaded by language servers, and files opened by other extensions. Use this for: 'what files is VS Code aware of?' Event: workspace.onDidOpenTextDocument. Warning: includes many files the developer hasn't explicitly opened.

Production Implementation

Here's the production TypeScript implementation for tracking all three file states:

// File state tracker — production implementation

interface FileState {

activeFile: string | undefined;

openTabs: string[];

loadedDocuments: string[];

}

function getFileState(): FileState {

const activeFile = vscode.window.activeTextEditor?.document.uri.fsPath;

const openTabs = vscode.window.tabGroups.all

.flatMap(group => group.tabs)

.filter(tab => tab.input instanceof vscode.TabInputText)

.map(tab => (tab.input as vscode.TabInputText).uri.fsPath);

const loadedDocuments = vscode.workspace.textDocuments

.filter(doc => doc.uri.scheme === 'file')

.map(doc => doc.uri.fsPath);

return { activeFile, openTabs, loadedDocuments };

}

The Event Subscription Pattern

For a context engine, you need real-time updates. Here's the event-driven pattern:

Step 01

Subscribe to Active Editor Changes

vscode.window.onDidChangeActiveTextEditor fires when the developer switches tabs. This is your highest-frequency, highest-priority event. Debounce at 100ms to avoid thrashing during rapid tab switches.

Step 02

Subscribe to Tab Changes

vscode.window.tabGroups.onDidChangeTabs fires when tabs are opened, closed, or moved. This updates your working set map. No debounce needed — tab events are infrequent.

Step 03

Subscribe to Document Changes

vscode.workspace.onDidChangeTextDocument fires on every edit. Use this for detecting modified files, not for tracking file state. Debounce at 500ms — edits fire hundreds of times per second during typing.

Step 04

Compute State on Every Event

On any event, recompute the full FileState object and emit it to your context engine. The computation takes <1ms. Don't try to incrementally update state — full recomputation is faster and more reliable.

Why This Matters for AI Context

The files a developer has open in their editor are the strongest signal of what's relevant to their current task. They opened those files intentionally. Tracking them accurately and continuously is the foundation of deterministic AI context.

🔧 This is how Context Snipe tracks your files.

Context Snipe's VS Code extension uses all three file tracking APIs to build a real-time picture of your IDE state. Open tabs, focused file, loaded documents — all fed into the AI context automatically. Start free — no credit card →