mirror of
https://github.com/Gmaker689/ai-diff-preview.git
synced 2026-10-07 01:31:51 +08:00
3168e81aa6
- 核心架构搭建:四层架构设计(触发层、隔离层、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
149 lines
3.5 KiB
TypeScript
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);
|
|
}
|
|
}
|
|
}
|
|
}
|