Build a VS Code Extension for Real-Time Code Context Capture & DB Integration (Phase 1)
Introduction
In today’s development landscape, providing real-time, context-aware code suggestions is crucial for productivity. This guide details Phase 1 of our project: building a VS Code extension that uses VS Code’s built-in language server to capture definitions and references from your codebase and store this context in a local SQLite database. The enriched context can help tools like GitHub Copilot debug and provide smarter suggestions.
Follow this step-by-step checklist to set up your environment, implement the extension, and verify that everything is working as expected.
Phase 1: Initial Extension Development & Database Integration
Goal:
Leverage VS Code’s language server to capture real-time code context and store the information in a local SQLite database.
1. Set Up Your Development Environment
- Install Node.js:
Download and install Node.js from nodejs.org. - Install VS Code:
Download and install Visual Studio Code from code.visualstudio.com. - Install TypeScript:
Open your terminal and run:npm install -g typescript
- Install Yeoman & VS Code Extension Generator:
Run the following command:npm install -g yo generator-code
- Create a New VS Code Extension Project:
In your terminal, run:yo code
Choose TypeScript as the language when prompted. This sets up the basic structure for your extension.
2. Project Structure & Dependencies
- Organize Your Project Folders:
Ensure your project contains directories such assrc/
for your TypeScript source files andout/
for the compiled JavaScript. - Configure
package.json
:
Add the required dependencies. Below is an example of a completepackage.json
:{ "name": "copilot-context-extension", "displayName": "Copilot Context Extension", "description": "Capture code context using VS Code’s language server and store it in a local SQLite database.", "version": "0.0.1", "engines": { "vscode": "^1.50.0" }, "activationEvents": [ "onCommand:extension.storeFunctionContext" ], "main": "./out/extension.js", "contributes": { "commands": [ { "command": "extension.storeFunctionContext", "title": "Store Function Context" } ] }, "scripts": { "vscode:prepublish": "npm run compile", "compile": "tsc -p ./" }, "dependencies": { "sqlite3": "^5.1.2" }, "devDependencies": { "typescript": "^4.0.3", "@types/node": "^12.11.7", "@types/vscode": "^1.50.0" } }
3. Implement Core Functionality
- Create the Command in
extension.ts
:
In yoursrc/extension.ts
file, implement the command that captures the symbol under the cursor and collects its definitions and references. Paste the following code intoextension.ts
:import * as vscode from 'vscode'; import * as sqlite3 from 'sqlite3'; let db: sqlite3.Database; // Initialize the SQLite database function initDatabase() { db = new sqlite3.Database('copilot_context.db', (err) => { if (err) { console.error("Failed to open database:", err.message); } else { db.run(`CREATE TABLE IF NOT EXISTS function_context ( id INTEGER PRIMARY KEY AUTOINCREMENT, function_name TEXT, file_path TEXT, line INTEGER, character INTEGER, context_type TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP )`, (err) => { if (err) { console.error("Failed to create table:", err.message); } }); } }); } // Store context data into the database function storeContext(functionName: string, filePath: string, line: number, character: number, contextType: string) { const stmt = db.prepare( `INSERT INTO function_context (function_name, file_path, line, character, context_type) VALUES (?, ?, ?, ?, ?)` ); stmt.run(functionName, filePath, line, character, contextType, (err) => { if (err) { console.error("Error storing context:", err.message); } }); stmt.finalize(); } export function activate(context: vscode.ExtensionContext) { // Initialize database upon activation initDatabase(); let disposable = vscode.commands.registerCommand('extension.storeFunctionContext', async () => { const editor = vscode.window.activeTextEditor; if (!editor) { vscode.window.showErrorMessage("No active editor found."); return; } const document = editor.document; const position = editor.selection.active; const wordRange = document.getWordRangeAtPosition(position); if (!wordRange) { vscode.window.showErrorMessage("No symbol selected."); return; } const selectedWord = document.getText(wordRange); // Retrieve definitions using VS Code's built-in command. const definitions = await vscode.commands.executeCommand<vscode.Location[]>( 'vscode.executeDefinitionProvider', document.uri, position ); // Retrieve references using VS Code's built-in command. const references = await vscode.commands.executeCommand<vscode.Location[]>( 'vscode.executeReferenceProvider', document.uri, position ); // Store definitions in the database. if (definitions && definitions.length > 0) { definitions.forEach(def => { storeContext( selectedWord, def.uri.fsPath, def.range.start.line + 1, def.range.start.character + 1, "definition" ); }); } else { vscode.window.showInformationMessage("No definitions found for " + selectedWord); } // Store references in the database. if (references && references.length > 0) { references.forEach(ref => { storeContext( selectedWord, ref.uri.fsPath, ref.range.start.line + 1, ref.range.start.character + 1, "reference" ); }); } else { vscode.window.showInformationMessage("No references found for " + selectedWord); } vscode.window.showInformationMessage(`Stored context for "${selectedWord}" in the database.`); }); context.subscriptions.push(disposable); } export function deactivate() { if (db) { db.close(); } }
- Configure the Command Trigger:
Ensure your command appears in the Command Palette or is accessible via a right-click/context menu as configured in yourpackage.json
.
4. Database Integration
- Database Initialization:
The above code creates (or opens) a SQLite database file namedcopilot_context.db
in the project’s root directory. - Create the Database Table:
A table calledfunction_context
is created with columns for:- Function name
- File path
- Line number
- Character position
- Context type (definition or reference)
- Timestamp
- Insert Captured Context:
ThestoreContext
function inserts each piece of captured data into the table.
5. Testing & Debugging
- Compile Your Extension:
Run the following command in your terminal:npm run compile
- Launch in Debug Mode:
Open VS Code, pressF5
to launch a new Extension Development Host. - Test the Functionality:
- Open a sample code file in the new window.
- Right-click on a function (or trigger via the keyboard shortcut) and select the “Store Function Context” command.
- Verify that the extension captures the symbol’s definitions and references, storing them in the database.
- Use VS Code’s Debugging Tools:
Check the debug console for errors or messages and set breakpoints inextension.ts
if needed.
6. Documentation & Final Checks
- Create a README File:
Include detailed instructions on:- Installing dependencies (
npm install
) - Compiling the project
- Launching the extension in debug mode
- How to trigger the command and check the database (
copilot_context.db
)
- Installing dependencies (
- Address Common Questions:
- Q: What if I don’t see any output?
A: Ensure the command is bound correctly and that you’re running the extension in the Extension Development Host. - Q: How do I view the database entries?
A: Use an SQLite viewer to open thecopilot_context.db
file located in the project folder. - Q: What if the database isn’t created?
A: Check the debug console for errors related to SQLite initialization or file permissions.
- Q: What if I don’t see any output?
- Final Testing:
Confirm that the extension passes the Testing & Debugging step and that GitHub Copilot can leverage the enriched context for better debugging.
Conclusion
By following this checklist, your team will have a fully functioning Phase 1 VS Code extension that captures real-time code context and stores it in a local database. This robust foundation not only enhances Copilot’s debugging capabilities but also sets the stage for future integration of historical change tracking and more advanced features.