Skip to content

MVP Implementation

Garot Conklin edited this page Jan 17, 2025 · 1 revision

MVP Implementation

Code Organization

The implementation follows a modular structure with clear separation of concerns:

// Extension entry point
export function activate(context: vscode.ExtensionContext): void {
    // Register commands
    const commands = [
        vscode.commands.registerCommand('cursor-rules-dynamic.showStatus', showStatus),
        vscode.commands.registerCommand('cursor-rules-dynamic.convertToJson', convertToJson),
        vscode.commands.registerCommand('cursor-rules-dynamic.browseTemplates', browseTemplates),
        vscode.commands.registerCommand('cursor-rules-dynamic.scanProject', scanProject)
    ];
    
    // Add to subscriptions
    context.subscriptions.push(...commands);
}

Core Components

1. Text Converter

export class TextConverter {
    public static async convertToJson(content: string): Promise<CursorRules> {
        return {
            version: '1.0.0',
            languages: {
                default: {
                    rules: [{
                        id: 'rule-1',
                        level: 'error',
                        description: content.trim()
                    }]
                }
            }
        };
    }

    public static async convertFile(uri: vscode.Uri): Promise<vscode.Uri> {
        // Implementation details
    }
}

2. Template Service

export class TemplateService {
    private readonly rulesPath: string;

    constructor(extensionPath: string) {
        this.rulesPath = path.join(extensionPath, 'rules');
    }

    public async getTemplateCategories(): Promise<string[]> {
        // Implementation details
    }

    public async getTemplatesInCategory(category: string): Promise<RuleTemplate[]> {
        // Implementation details
    }
}

3. Project Scanner

async function analyzeProject(): Promise<ProjectAnalysis> {
    const workspaceFiles = await vscode.workspace.findFiles('**/*');
    const analysis: ProjectAnalysis = {
        codePatterns: {
            naming: { variables: [], functions: [], classes: [] },
            structure: { directories: [], fileTypes: [] },
            conventions: { formatting: [], documentation: [], testing: [] }
        },
        timestamp: new Date().toISOString()
    };

    // Implementation details
    return analysis;
}

Key Features Implementation

1. Dynamic Analysis

function analyzeNamingConventions(content: string, analysis: ProjectAnalysis): void {
    // Function names
    const functionMatches = content.match(/function\s+([a-zA-Z][a-zA-Z0-9]*)/g);
    if (functionMatches) {
        functionMatches.forEach(match => {
            const name = match.replace('function ', '');
            if (!analysis.codePatterns.naming.functions.includes(name)) {
                analysis.codePatterns.naming.functions.push(name);
            }
        });
    }

    // Class names
    const classMatches = content.match(/class\s+([a-zA-Z][a-zA-Z0-9]*)/g);
    if (classMatches) {
        classMatches.forEach(match => {
            const name = match.replace('class ', '');
            if (!analysis.codePatterns.naming.classes.includes(name)) {
                analysis.codePatterns.naming.classes.push(name);
            }
        });
    }
}

2. File Monitoring

const watcher = vscode.workspace.createFileSystemWatcher('**/*');
watcher.onDidChange((uri) => {
    try {
        const message = `Processing file: ${uri.fsPath}`;
        console.log(message);
        void vscode.window.showInformationMessage(message);
        updateStatusBar(statusBarItem);
    } catch (error) {
        console.error('Error processing file:', error);
    }
});

3. Template Management

public async saveTemplateToWorkspace(template: RuleTemplate, targetUri?: vscode.Uri): Promise<void> {
    try {
        if (!targetUri) {
            const workspaceFolders = vscode.workspace.workspaceFolders;
            if (!workspaceFolders) {
                throw new Error('No workspace folder found');
            }
            targetUri = vscode.Uri.joinPath(workspaceFolders[0].uri, '.cursorrules');
        }

        // Convert template content
        const convertedContent = this.convertTemplateContent(template.content);

        // Write content
        await vscode.workspace.fs.writeFile(targetUri, Buffer.from(convertedContent, 'utf-8'));
        
        // Show success message
        void vscode.window.showInformationMessage('Template saved successfully');
        
        // Open the file
        const document = await vscode.workspace.openTextDocument(targetUri);
        await vscode.window.showTextDocument(document);
    } catch (error) {
        throw new Error(`Failed to save template: ${error instanceof Error ? error.message : String(error)}`);
    }
}

Testing Implementation

1. Unit Tests

suite('TextConverter Test Suite', () => {
    test('should convert text to JSON format', async () => {
        const content = 'Test content';
        const result = await TextConverter.convertToJson(content);
        assert.ok(result.languages.default, 'Should have default rules');
        assert.strictEqual(result.languages.default.rules[0].description, content, 'Should preserve content');
    });
});

2. Integration Tests

suite('Extension Test Suite', () => {
    test('Extension activation should register commands', async () => {
        await new Promise(resolve => setTimeout(resolve, 1000));
        
        const commands = await vscode.commands.getCommands();
        assert.ok(
            commands.includes('cursor-rules-dynamic.showStatus'),
            'Show status command should be registered'
        );
        assert.ok(
            commands.includes('cursor-rules-dynamic.scanProject'),
            'Scan project command should be registered'
        );
    });
});

Error Handling

try {
    await operation();
} catch (error) {
    void vscode.window.showErrorMessage(
        `Failed to perform operation: ${error instanceof Error ? error.message : String(error)}`,
        { modal: true }
    );
}

Performance Considerations

// Debounced file watcher
let timeout: NodeJS.Timeout | undefined;
watcher.onDidChange((uri) => {
    if (timeout) {
        clearTimeout(timeout);
    }
    timeout = setTimeout(() => {
        processFileChange(uri);
    }, 500);
});

Security Implementation

private static async ensureBackupDirectory(workspaceUri: vscode.Uri): Promise<vscode.Uri> {
    const backupDirUri = vscode.Uri.joinPath(workspaceUri, '.cursorrules-backup');
    try {
        await vscode.workspace.fs.stat(backupDirUri);
    } catch {
        await vscode.workspace.fs.createDirectory(backupDirUri);
    }
    return backupDirUri;
}

Documentation Generation

/**
 * Converts markdown-formatted .cursorrules content to JSON format
 * @param content The markdown content to convert
 * @returns The converted JSON content as a CursorRules object
 */
public static convertToJson(content: string): Promise<CursorRules> {
    // Implementation
}