codeflash-internal/js/VSC-Extension/EXTENSION_GUIDE.md
mohammed ahmed 549b42b6f1
[VSC] React sidebar & reduce bundle size (#1761)
depends on https://github.com/codeflash-ai/codeflash/pull/688,
https://github.com/codeflash-ai/codeflash-internal/pull/1767

---------

Signed-off-by: ali <mohammed18200118@gmail.com>
Co-authored-by: Sarthak Agarwal <sarthak.saga@gmail.com>
2025-09-04 18:58:54 +05:30

817 lines
21 KiB
Markdown

# Complete VSCode Extension Development Guide: Codeflash
## Table of Contents
1. [Extension Architecture Overview](#extension-architecture-overview)
2. [Project Structure & Files](#project-structure--files)
3. [Core Concepts & Technologies](#core-concepts--technologies)
4. [Detailed Component Analysis](#detailed-component-analysis)
5. [VSCode Extension APIs Used](#vscode-extension-apis-used)
6. [Decision Making Process](#decision-making-process)
7. [Build System & Tooling](#build-system--tooling)
8. [Extension Lifecycle](#extension-lifecycle)
---
## Extension Architecture Overview
### High-Level Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ VSCode Extension Host │
├─────────────────────────────────────────────────────────────┤
│ Extension.ts (Main Entry Point) │
│ ├── Registers Providers & Commands │
│ ├── Manages Service Lifecycle │
│ └── Handles Extension Activation/Deactivation │
├─────────────────────────────────────────────────────────────┤
│ Providers (UI Components) │
│ ├── SidebarProvider (Webview) │
│ └── CodeLensProvider (Inline Buttons) │
├─────────────────────────────────────────────────────────────┤
│ Services (Business Logic) │
│ ├── LSP Service (Language Server Communication) │
│ ├── Analysis Service (Code Analysis) │
│ ├── Optimization Service (Code Optimization) │
│ ├── Diff Service (Diff View Management) │
│ └── Python Service (Environment Detection) │
├─────────────────────────────────────────────────────────────┤
│ Utils & Constants │
│ ├── Logger (Centralized Logging) │
│ ├── Error Handling │
│ └── Path Utilities │
└─────────────────────────────────────────────────────────────┘
```
### Why This Architecture?
- **Separation of Concerns**: UI (Providers) separated from business logic (Services)
- **Testability**: Each service can be tested independently
- **Maintainability**: Clear boundaries between components
- **Scalability**: Easy to add new providers or services
---
## Project Structure & Files
### Core Files Analysis
#### `package.json` - Extension Manifest
```json
{
"name": "codeflash",
"displayName": "Codeflash",
"description": "Optimize Your Code - Automatically",
"main": "./dist/extension.js",
"engines": { "vscode": "^1.98.0" }
}
```
**Key Decisions:**
- **`main`**: Points to compiled JavaScript, not TypeScript source
- **`engines.vscode`**: Minimum VSCode version required
- **`activationEvents: []`**: Empty array means activate on any event (modern approach)
#### `contributes` Section - VSCode Integration Points
```json
"contributes": {
"viewsContainers": { /* Custom sidebar container */ },
"views": { /* Sidebar webview */ },
"commands": { /* Extension commands */ },
"menus": { /* Command palette & context menus */ },
"keybindings": { /* Keyboard shortcuts */ }
}
```
**Why Each Contribution:**
- **viewsContainers**: Creates our own sidebar section (not mixed with Explorer/Git)
- **commands**: Defines callable actions from command palette
- **menus**: Controls where commands appear (command palette, context menus)
- **keybindings**: Provides keyboard shortcuts for power users
### TypeScript Configuration
#### `tsconfig.json` - Compilation Settings
```json
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"lib": ["ES2020"],
"strict": true
}
}
```
**Decisions:**
- **ES2020**: Modern JavaScript features while maintaining compatibility
- **commonjs**: Required for Node.js/VSCode extension environment
- **strict: true**: Enables all TypeScript strict type checking
---
## Core Concepts & Technologies
### 1. Language Server Protocol (LSP)
**What is LSP?**
Language Server Protocol is a standardized way for editors to communicate with language analysis tools.
**Implementation in Codeflash:**
```typescript
// src/services/lspService.ts
export class LspService {
async startClient(pythonPath: string): Promise<LanguageClient> {
const client = new LanguageClient(
LSP_CLIENT_ID,
LSP_CLIENT_NAME,
serverOptions,
clientOptions,
);
await client.start();
return client;
}
}
```
**Why LSP?**
- **Standardized**: Works with any LSP-compliant language server
- **Performant**: Separate process handles heavy analysis
- **Extensible**: Can add new LSP commands easily
### 2. Webview API
**What are Webviews?**
Webviews let you display custom HTML/CSS/JavaScript UI within VSCode.
**Implementation:**
```typescript
// src/providers/SidebarProvider.ts
export class SidebarProvider implements vscode.WebviewViewProvider {
resolveWebviewView(webviewView: vscode.WebviewView) {
webviewView.webview.html = generateWebviewHtml(/* ... */);
webviewView.webview.onDidReceiveMessage(this.handleWebviewMessage);
}
}
```
**Security Considerations:**
- **Content Security Policy**: Prevents XSS attacks
- **localResourceRoots**: Limits file access
- **enableScripts**: Explicitly enables JavaScript
### 3. CodeLens API
**What is CodeLens?**
CodeLens provides inline actionable information in the editor.
**Implementation:**
```typescript
// src/providers/CodeLensProvider.ts
export class CodeflashCodeLensProvider implements vscode.CodeLensProvider {
async provideCodeLenses(
document: vscode.TextDocument,
): Promise<vscode.CodeLens[]> {
// Analyze document and return CodeLens items
return codeLenses;
}
}
```
**Why CodeLens?**
- **Contextual**: Appears exactly where it's relevant
- **Non-intrusive**: Doesn't modify the actual code
- **Discoverable**: Users see optimization opportunities immediately
---
## Detailed Component Analysis
### Extension Entry Point (`src/extension.ts`)
#### Activation Function
```typescript
export async function activate(
context: vscode.ExtensionContext,
): Promise<void> {
// 1. Initialize services
const pythonService = new PythonService();
const lspService = new LspService();
// 2. Start language client
const client = await lspService.startClient(pythonPath);
// 3. Register providers
const sidebarProvider = new SidebarProvider(context.extensionUri, client);
const codeLensProvider = new CodeflashCodeLensProvider(
client,
analysisService,
);
// 4. Register with VSCode
context.subscriptions.push(
vscode.window.registerWebviewViewProvider(SIDEBAR_VIEW_ID, sidebarProvider),
vscode.languages.registerCodeLensProvider(
{ language: "python" },
codeLensProvider,
),
);
}
```
**Key Concepts:**
- **ExtensionContext**: Provides extension lifetime management
- **subscriptions**: Automatic cleanup when extension deactivates
- **Dependency Injection**: Services receive their dependencies
#### Command Registration
```typescript
const optimizeFunctionCommand = vscode.commands.registerCommand(
"codeflash.optimizeFunction",
async (uri: vscode.Uri, functionName: string) => {
await handleOptimizeFunction(uri, functionName);
},
);
```
**Command Design Patterns:**
- **Async handlers**: All operations are asynchronous
- **Typed parameters**: Commands receive strongly-typed parameters
- **Error boundaries**: Each command has error handling
### Sidebar Provider (`src/providers/SidebarProvider.ts`)
#### Webview Communication Pattern
```typescript
// Extension → Webview
this.sendMessage({
type: "updateState",
payload: { status, message, functionData },
});
// Webview → Extension
webviewView.webview.onDidReceiveMessage(async (data) => {
switch (data.type) {
case "requestAnalysis":
await this.handleRequestAnalysis();
break;
}
});
```
**Message Types:**
- **updateState**: Updates UI state (loading, success, error)
- **optimizationStarted**: Shows progress overlay
- **optimizationComplete**: Shows results
**State Management:**
- **Centralized**: All state changes go through `updateWebviewState`
- **Reactive**: UI updates automatically when state changes
- **Persistent**: State survives webview reloads
### CodeLens Provider (`src/providers/CodeLensProvider.ts`)
#### Function Detection Algorithm
```typescript
private findFunctionPosition(lines: string[], functionName: string): Position | null {
const isMethod = functionName.includes('.');
if (isMethod) {
const [className, methodName] = functionName.split('.');
// 1. Find class definition
// 2. Find method within class
} else {
// Find standalone function
}
}
```
**Why This Approach?**
- **Handles both functions and methods**: Single algorithm for all cases
- **Line-by-line parsing**: Simple and reliable
- **Position accuracy**: Returns exact character position for button placement
#### CodeLens Refresh Strategy
```typescript
// Refresh triggers
this._lspClient.onDidChangeState(() => this._onDidChangeCodeLenses.fire());
vscode.workspace.onDidSaveTextDocument(() =>
this._onDidChangeCodeLenses.fire(),
);
```
**Refresh Strategy:**
- **LSP state changes**: When language server starts/stops
- **File saves**: When user saves Python files
- **Manual refresh**: Via command palette
### Services Architecture
#### Service Communication Pattern
```typescript
// Service dependencies are injected
export class AnalysisService {
constructor(private client: LanguageClient) {}
async getOptimizableFunctions(uri: vscode.Uri) {
return await this.client.sendRequest(
LSP_COMMANDS.GET_OPTIMIZABLE_FUNCTIONS,
{
textDocument: { uri: uri.toString() },
},
);
}
}
```
**Design Principles:**
- **Single Responsibility**: Each service has one clear purpose
- **Dependency Injection**: Services receive dependencies in constructor
- **Interface Segregation**: Services expose minimal public APIs
#### Error Handling Strategy
```typescript
try {
const result = await this.optimizationService.optimizeFunction(
functionName,
uri,
);
// Handle success
} catch (error) {
this.logger.error(`Error optimizing function ${functionName}`, error);
vscode.window.showErrorMessage(`Failed to optimize ${functionName}`);
}
```
**Error Handling Layers:**
1. **Service Level**: Catch and log errors
2. **UI Level**: Show user-friendly messages
3. **Global Level**: Extension-wide error boundaries
---
## VSCode Extension APIs Used
### Core APIs
#### 1. Commands API
```typescript
// Register command
vscode.commands.registerCommand("codeflash.optimizeFunction", handler);
// Execute command
vscode.commands.executeCommand("vscode.diff", originalUri, optimizedUri, title);
```
**Use Cases:**
- **User-triggered actions**: Optimize function, refresh analysis
- **Internal operations**: Open diff view, navigate to function
#### 2. Language Features API
```typescript
// CodeLens provider
vscode.languages.registerCodeLensProvider(selector, provider);
// Document selectors
{ language: 'python', scheme: 'file' }
```
**Selector Strategy:**
- **language**: Only Python files
- **scheme**: Only file system files (not git:// or other schemes)
#### 3. Workspace API
```typescript
// File operations
vscode.workspace.openTextDocument(uri);
vscode.workspace.fs.writeFile(uri, content);
// Event listening
vscode.workspace.onDidSaveTextDocument(handler);
```
#### 4. Window API
```typescript
// User interaction
vscode.window.showInformationMessage(message, ...actions);
vscode.window.withProgress(options, handler);
// Editor operations
vscode.window.showTextDocument(document, options);
```
### Advanced APIs
#### 1. Webview API
```typescript
// Security configuration
webview.options = {
enableScripts: true,
localResourceRoots: [extensionUri, nodeModulesUri]
};
// Content Security Policy
<meta http-equiv="Content-Security-Policy" content="
default-src 'none';
script-src ${webview.cspSource} 'nonce-${nonce}';
style-src ${webview.cspSource} 'unsafe-inline';
">
```
#### 2. Language Client API (from vscode-languageclient)
```typescript
const client = new LanguageClient(id, name, serverOptions, clientOptions);
await client.start();
const result = await client.sendRequest(method, params);
```
---
## Decision Making Process
### Technology Choices
#### 1. TypeScript vs JavaScript
**Chosen: TypeScript**
**Reasons:**
- **Type Safety**: Catches errors at compile time
- **IDE Support**: Better IntelliSense and refactoring
- **Maintainability**: Self-documenting code with types
- **VSCode Standard**: Most VSCode extensions use TypeScript
#### 2. ESBuild vs Webpack
**Chosen: ESBuild**
**Reasons:**
- **Speed**: 10-100x faster than Webpack
- **Simplicity**: Minimal configuration required
- **Size**: Smaller bundle sizes
- **Modern**: Built for modern JavaScript/TypeScript
#### 3. Webview vs TreeView for Sidebar
**Chosen: Webview**
**Reasons:**
- **Flexibility**: Complete control over UI
- **Rich Interactions**: Complex UI elements (progress bars, buttons)
- **Styling**: Custom CSS for branding
- **Future-proof**: Can add any web technology
#### 4. CodeLens vs Decorations for Inline UI
**Chosen: CodeLens**
**Reasons:**
- **Clickable**: CodeLens supports commands, decorations don't
- **Standard**: Users familiar with CodeLens from other extensions
- **Semantic**: CodeLens is meant for actionable metadata
- **Positioning**: Automatically positioned above code
### Architectural Decisions
#### 1. Service-Based Architecture
**Why:**
- **Testability**: Services can be unit tested independently
- **Reusability**: Services can be used by multiple providers
- **Separation**: UI logic separated from business logic
#### 2. LSP Communication
**Why:**
- **Performance**: Heavy analysis runs in separate process
- **Language Agnostic**: Same extension could support multiple languages
- **Standardized**: Uses industry-standard protocol
#### 3. Message-Based Webview Communication
**Why:**
- **Security**: No direct object sharing between contexts
- **Reliability**: Structured communication reduces bugs
- **Debuggability**: All messages can be logged
---
## Build System & Tooling
### ESBuild Configuration (`esbuild.js`)
```javascript
const esbuild = require("esbuild");
const production = process.argv.includes("--production");
const watch = process.argv.includes("--watch");
const ctx = await esbuild.context({
entryPoints: ["src/extension.ts"],
bundle: true,
format: "cjs",
minify: production,
sourcemap: !production,
platform: "node",
outdir: "dist",
external: ["vscode"],
});
```
**Configuration Explained:**
- **bundle: true**: Combines all files into single output
- **format: 'cjs'**: CommonJS format for Node.js
- **external: ['vscode']**: VSCode API provided by host
- **platform: 'node'**: Target Node.js environment
### ESLint Configuration (`eslint.config.mjs`)
```javascript
export default [
{
files: ["src/**/*.ts"],
languageOptions: {
parser: tsParser,
parserOptions: { project: "./tsconfig.json" },
},
rules: {
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "warn",
},
},
];
```
### Development Scripts
```json
{
"dev": "npm run compile && npm run watch",
"build": "npm run check-types && npm run lint && node esbuild.js",
"package": "npm run build --production"
}
```
**Script Strategy:**
- **dev**: Development with watch mode
- **build**: Full build with type checking and linting
- **package**: Production build with minification
---
## Extension Lifecycle
### 1. Activation Sequence
```
1. VSCode loads extension.js
2. activate() function called
3. Services initialized
4. LSP client started
5. Providers registered
6. Commands registered
7. Extension ready
```
### 2. Runtime Operations
```
User opens Python file
├── CodeLens provider activated
├── Analysis service queries LSP
├── CodeLens buttons appear
User clicks "Optimize with CF"
├── Command handler invoked
├── Optimization service called
├── Progress indicator shown
├── Diff service opens results
└── User accepts/rejects changes
```
### 3. Deactivation Sequence
```
1. deactivate() function called
2. LSP client stopped
3. Services disposed
4. Event listeners removed
5. Resources cleaned up
```
---
## Advanced Concepts
### Memory Management
```typescript
export async function deactivate(): Promise<void> {
// Dispose services in reverse order
codeLensProvider?.dispose();
diffService?.dispose();
await lspService?.stopClient();
// Clear references to prevent memory leaks
codeLensProvider = undefined;
diffService = undefined;
}
```
### Error Boundaries
```typescript
// Global error handling
process.on("uncaughtException", (error) => {
logger?.error("Uncaught exception", error);
});
// Service-level error handling
async function handleOptimizeFunction(uri: vscode.Uri, functionName: string) {
try {
// Operation
} catch (error) {
logger?.error(`Error optimizing function ${functionName}`, error);
vscode.window.showErrorMessage(`Failed to optimize ${functionName}`);
}
}
```
### Performance Optimization
```typescript
// Debounced CodeLens refresh
private refreshDebounced = debounce(() => {
this._onDidChangeCodeLenses.fire();
}, 500);
// Lazy loading
async provideCodeLenses(document: vscode.TextDocument) {
if (this.client.state !== LanguageClientState.Running) {
return []; // Early return if not ready
}
// Expensive operations only when needed
}
```
---
## Testing Strategy
### Unit Testing Example
```typescript
describe("AnalysisService", () => {
let service: AnalysisService;
let mockClient: jest.Mocked<LanguageClient>;
beforeEach(() => {
mockClient = createMockLanguageClient();
service = new AnalysisService(mockClient);
});
it("should return optimizable functions", async () => {
mockClient.sendRequest.mockResolvedValue({
"file:///test.py": ["function1", "Class.method1"],
});
const result = await service.getOptimizableFunctions(testUri);
expect(result.status).toBe("success");
expect(result.functions).toHaveLength(2);
});
});
```
### Integration Testing
```typescript
describe("Extension Integration", () => {
it("should activate successfully", async () => {
const context = createMockExtensionContext();
await activate(context);
expect(context.subscriptions).toHaveLength(5);
expect(vscode.commands.registerCommand).toHaveBeenCalled();
});
});
```
---
## Best Practices Applied
### 1. Security
- **CSP**: Content Security Policy for webviews
- **Input validation**: All user inputs validated
- **Resource isolation**: Webview resources restricted
### 2. Performance
- **Lazy loading**: Components loaded only when needed
- **Debouncing**: Frequent operations debounced
- **Caching**: Results cached when appropriate
### 3. User Experience
- **Progressive disclosure**: Simple UI with advanced features available
- **Feedback**: Loading states and progress indicators
- **Accessibility**: Proper ARIA labels and keyboard navigation
### 4. Maintainability
- **Type safety**: Comprehensive TypeScript types
- **Documentation**: Code comments and this guide
- **Testing**: Unit and integration tests
---
## Common Patterns in VSCode Extensions
### 1. Provider Pattern
```typescript
interface MyProvider {
provideX(document: TextDocument): X[];
}
vscode.languages.registerXProvider(selector, provider);
```
### 2. Command Pattern
```typescript
const command = vscode.commands.registerCommand("extension.action", (args) => {
// Handle command
});
```
### 3. Event-Driven Architecture
```typescript
vscode.workspace.onDidChangeTextDocument((event) => {
// React to changes
});
```
### 4. Disposable Pattern
```typescript
class MyService implements vscode.Disposable {
private disposables: vscode.Disposable[] = [];
dispose() {
vscode.Disposable.from(...this.disposables).dispose();
}
}
```
---
This guide covers the fundamental concepts and decisions behind building a professional VSCode extension. Each component serves a specific purpose and follows established patterns for maintainability and user experience.