/** * 文件变更追踪服务 * 功能:收集和管理 AI 修改文件的变更记录 * 依赖:types/fileChanges * 使用场景:在文件操作时记录变更,供用户审查 */ import { FileChange, ChangeSession } from '../types/fileChanges'; import * as vscode from 'vscode'; import * as fs from 'fs'; import * as path from 'path'; class ChangeTrackerService { private currentSession: ChangeSession | null = null; private changeListeners: Array<(session: ChangeSession) => void> = []; /** * 开始新的变更会话 */ startSession(sessionId: string): void { // 如果已有 session(无论状态),重用并重置为 active if (this.currentSession) { this.currentSession.status = 'active'; return; } this.currentSession = { sessionId, startTime: Date.now(), changes: [], status: 'active' }; } /** * 记录文件变更 */ trackChange(filePath: string, oldContent: string, newContent: string): string { if (!this.currentSession) { this.startSession(`session_${Date.now()}`); } const changeId = `change_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; // 判断变更类型 let changeType: 'create' | 'modify' | 'delete'; if (oldContent === '' && newContent !== '') { changeType = 'create'; } else if (oldContent !== '' && newContent === '') { changeType = 'delete'; } else { changeType = 'modify'; } const change: FileChange = { filePath, oldContent, newContent, timestamp: Date.now(), changeType, changeId }; this.currentSession!.changes.push(change); this.notifyListeners(); return changeId; } /** * 结束当前会话 */ endSession(): ChangeSession | null { if (this.currentSession && this.currentSession.changes.length > 0) { this.currentSession.status = 'completed'; const session = this.currentSession; this.notifyListeners(); return session; } return null; } /** * 获取当前会话 */ getCurrentSession(): ChangeSession | null { return this.currentSession; } /** * 清空当前会话 */ clearSession(): void { this.currentSession = null; this.notifyListeners(); } /** * 移除指定的变更 */ removeChange(changeId: string): boolean { if (!this.currentSession) { return false; } const index = this.currentSession.changes.findIndex(c => c.changeId === changeId); if (index !== -1) { this.currentSession.changes.splice(index, 1); this.notifyListeners(); return true; } return false; } /** * 监听变更 */ onChangeUpdate(listener: (session: ChangeSession) => void): void { this.changeListeners.push(listener); } /** * 通知所有监听器 */ private notifyListeners(): void { if (this.currentSession) { this.changeListeners.forEach(listener => listener(this.currentSession!)); } } /** * 采纳变更(保存文件) */ async acceptChange(changeId: string): Promise { if (!this.currentSession) { return false; } const change = this.currentSession.changes.find(c => c.changeId === changeId); if (!change) { return false; } try { const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; if (!workspaceFolder) { return false; } const absolutePath = path.join(workspaceFolder.uri.fsPath, change.filePath); // 如果是删除操作,删除文件 if (change.changeType === 'delete') { if (fs.existsSync(absolutePath)) { await fs.promises.unlink(absolutePath); } } else { // 创建或修改文件 await fs.promises.writeFile(absolutePath, change.newContent, 'utf-8'); } this.removeChange(changeId); return true; } catch (error) { console.error('[ChangeTracker] 采纳变更失败:', error); return false; } } /** * 拒绝变更(恢复旧内容) */ async rejectChange(changeId: string): Promise { if (!this.currentSession) { return false; } const change = this.currentSession.changes.find(c => c.changeId === changeId); if (!change) { return false; } try { const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; if (!workspaceFolder) { return false; } const absolutePath = path.join(workspaceFolder.uri.fsPath, change.filePath); // 如果是新建文件,删除它 if (change.changeType === 'create') { if (fs.existsSync(absolutePath)) { await fs.promises.unlink(absolutePath); } } else { // 恢复旧内容 await fs.promises.writeFile(absolutePath, change.oldContent, 'utf-8'); } this.removeChange(changeId); return true; } catch (error) { console.error('[ChangeTracker] 拒绝变更失败:', error); return false; } } } export const changeTracker = new ChangeTrackerService();