Files
ai-diff-preview/src/snapshot/snapshotManager.ts
T
Gmarker689 3168e81aa6 feat: 初始化 AI Code Diff Preview 插件项目
- 核心架构搭建:四层架构设计(触发层、隔离层、Diff引擎、UI层)
- 快照管理器:管理 Base/Suggest 双版本,隔离 AI 生成代码
- Diff 引擎:基于 Myers 算法计算差异,生成变更块
- 内联装饰器:红绿高亮显示删除/新增行
- CodeLens 提供器:提供 Accept/Reject 按钮
- 事务处理:Accept/Reject 原子化操作
- Claude Hooks 集成:支持 after_code_edit/after_write_file 事件
- 状态栏显示:实时显示变更状态
- 基础测试用例:7 个测试全部通过

技术栈:TypeScript + VSCode Extension API + diff 库 + esbuild
2026-06-17 21:11:33 +08:00

149 lines
3.5 KiB
TypeScript

/**
* 快照管理器 - 管理文件的 Base/Suggest 双版本
*/
import { v4 as uuidv4 } from 'uuid';
import { Snapshot, DiffChunk, ChunkStatus } from '../models/types';
export class SnapshotManager {
private snapshots: Map<string, Snapshot> = new Map();
private filePathIndex: Map<string, string> = new Map(); // filePath -> snapshotId
/**
* 创建 Base 快照
*/
createBaseSnapshot(filePath: string, baseContent: string): string {
const id = uuidv4();
const snapshot: Snapshot = {
id,
filePath,
baseContent,
suggestContent: '',
timestamp: Date.now(),
chunks: [],
status: 'active',
};
this.snapshots.set(id, snapshot);
this.filePathIndex.set(filePath, id);
return id;
}
/**
* 应用 AI 生成的 Patch
*/
applyPatch(snapshotId: string, patch: string, chunks: DiffChunk[]): void {
const snapshot = this.snapshots.get(snapshotId);
if (!snapshot) {
throw new Error(`快照不存在: ${snapshotId}`);
}
snapshot.suggestContent = patch;
snapshot.chunks = chunks;
}
/**
* 获取快照
*/
getSnapshot(snapshotId: string): Snapshot | undefined {
return this.snapshots.get(snapshotId);
}
/**
* 根据文件路径获取快照
*/
getSnapshotByFilePath(filePath: string): Snapshot | undefined {
const snapshotId = this.filePathIndex.get(filePath);
if (!snapshotId) return undefined;
return this.snapshots.get(snapshotId);
}
/**
* 更新变更块状态
*/
updateChunkStatus(snapshotId: string, chunkId: string, status: ChunkStatus): void {
const snapshot = this.snapshots.get(snapshotId);
if (!snapshot) return;
const chunk = snapshot.chunks.find(c => c.id === chunkId);
if (chunk) {
chunk.status = status;
}
}
/**
* 批量更新变更块状态
*/
updateAllChunksStatus(snapshotId: string, status: ChunkStatus): void {
const snapshot = this.snapshots.get(snapshotId);
if (!snapshot) return;
snapshot.chunks.forEach(chunk => {
chunk.status = status;
});
}
/**
* 标记快照完成
*/
completeSnapshot(snapshotId: string): void {
const snapshot = this.snapshots.get(snapshotId);
if (!snapshot) return;
snapshot.status = 'completed';
this.filePathIndex.delete(snapshot.filePath);
}
/**
* 取消快照
*/
cancelSnapshot(snapshotId: string): void {
const snapshot = this.snapshots.get(snapshotId);
if (!snapshot) return;
snapshot.status = 'cancelled';
this.filePathIndex.delete(snapshot.filePath);
}
/**
* 删除快照
*/
deleteSnapshot(snapshotId: string): void {
const snapshot = this.snapshots.get(snapshotId);
if (snapshot) {
this.filePathIndex.delete(snapshot.filePath);
}
this.snapshots.delete(snapshotId);
}
/**
* 获取所有活跃快照
*/
getActiveSnapshots(): Snapshot[] {
return Array.from(this.snapshots.values()).filter(s => s.status === 'active');
}
/**
* 检查文件是否有活跃快照
*/
hasActiveSnapshot(filePath: string): boolean {
const snapshotId = this.filePathIndex.get(filePath);
if (!snapshotId) return false;
const snapshot = this.snapshots.get(snapshotId);
return snapshot?.status === 'active';
}
/**
* 清理已完成或取消的快照
*/
cleanup(): void {
for (const [id, snapshot] of this.snapshots.entries()) {
if (snapshot.status !== 'active') {
this.filePathIndex.delete(snapshot.filePath);
this.snapshots.delete(id);
}
}
}
}