mirror of
https://github.com/Gmaker689/ai-diff-preview.git
synced 2026-09-27 03:21:52 +08:00
fix: Diff状态栏按钮原子性保证 — isOpening锁防竞态 + session先于vscode.diff设置
This commit is contained in:
+21
-42
@@ -5,7 +5,7 @@
|
|||||||
* 1. 临时文件(非 untitled)避免保存提示
|
* 1. 临时文件(非 untitled)避免保存提示
|
||||||
* 2. ★ 临时文件只在 Accept/Reject 后删除,手动关闭 Diff 不影响
|
* 2. ★ 临时文件只在 Accept/Reject 后删除,手动关闭 Diff 不影响
|
||||||
* 3. Accept/Reject 后自动推进到下一个 pending 文件
|
* 3. Accept/Reject 后自动推进到下一个 pending 文件
|
||||||
* 4. 状态栏按钮在 Diff 模式下可见
|
* 4. 状态栏按钮在 Diff 模式下可见(isOpening 锁防竞态)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as vscode from 'vscode';
|
import * as vscode from 'vscode';
|
||||||
@@ -27,8 +27,9 @@ export interface DiffSession {
|
|||||||
|
|
||||||
export class DiffViewer {
|
export class DiffViewer {
|
||||||
private activeSession: DiffSession | null = null;
|
private activeSession: DiffSession | null = null;
|
||||||
private closeListener: vscode.Disposable | null = null;
|
|
||||||
private tmpFiles: string[] = [];
|
private tmpFiles: string[] = [];
|
||||||
|
/** ★ 原子锁:Diff 正在打开中,禁止清除 session */
|
||||||
|
private isOpening = false;
|
||||||
|
|
||||||
/** 外部回调:Diff 操作完成后通知(用于刷新 UI) */
|
/** 外部回调:Diff 操作完成后通知(用于刷新 UI) */
|
||||||
onDiffAction?: () => void;
|
onDiffAction?: () => void;
|
||||||
@@ -49,23 +50,24 @@ export class DiffViewer {
|
|||||||
const beforePath = this.makeTmpFile(`ai-diff-before-${editRecord.id.slice(0, 8)}${ext}`, editRecord.beforeContent);
|
const beforePath = this.makeTmpFile(`ai-diff-before-${editRecord.id.slice(0, 8)}${ext}`, editRecord.beforeContent);
|
||||||
const afterPath = this.makeTmpFile(`ai-diff-after-${editRecord.id.slice(0, 8)}${ext}`, editRecord.afterContent);
|
const afterPath = this.makeTmpFile(`ai-diff-after-${editRecord.id.slice(0, 8)}${ext}`, editRecord.afterContent);
|
||||||
|
|
||||||
this.setSession({
|
this.isOpening = true;
|
||||||
|
this.activeSession = {
|
||||||
fileId: fileChangeId,
|
fileId: fileChangeId,
|
||||||
editId: editRecord.id,
|
editId: editRecord.id,
|
||||||
beforeTmpPath: beforePath,
|
beforeTmpPath: beforePath,
|
||||||
afterTmpPath: afterPath,
|
afterTmpPath: afterPath,
|
||||||
beforeUri: vscode.Uri.file(beforePath),
|
beforeUri: vscode.Uri.file(beforePath),
|
||||||
afterUri: vscode.Uri.file(afterPath),
|
afterUri: vscode.Uri.file(afterPath),
|
||||||
});
|
};
|
||||||
|
|
||||||
await vscode.commands.executeCommand(
|
await vscode.commands.executeCommand(
|
||||||
'vscode.diff',
|
'vscode.diff',
|
||||||
this.activeSession!.beforeUri,
|
this.activeSession.beforeUri,
|
||||||
this.activeSession!.afterUri,
|
this.activeSession.afterUri,
|
||||||
`${fileName} — AI ${toolLabel} #${editRecord.id.slice(0, 6)} · 状态栏操作`
|
`${fileName} — AI ${toolLabel} #${editRecord.id.slice(0, 6)} · 状态栏操作`
|
||||||
);
|
);
|
||||||
|
|
||||||
// ★ 通知外部刷新状态栏(切换到 Diff 模式显示 Accept/Reject 按钮)
|
this.isOpening = false;
|
||||||
this.onDiffAction?.();
|
this.onDiffAction?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,22 +78,23 @@ export class DiffViewer {
|
|||||||
const beforePath = this.makeTmpFile(`ai-diff-before-${fileChange.id.slice(0, 8)}${ext}`, fileChange.originalContent);
|
const beforePath = this.makeTmpFile(`ai-diff-before-${fileChange.id.slice(0, 8)}${ext}`, fileChange.originalContent);
|
||||||
const afterPath = this.makeTmpFile(`ai-diff-after-${fileChange.id.slice(0, 8)}${ext}`, fileChange.latestContent);
|
const afterPath = this.makeTmpFile(`ai-diff-after-${fileChange.id.slice(0, 8)}${ext}`, fileChange.latestContent);
|
||||||
|
|
||||||
this.setSession({
|
this.isOpening = true;
|
||||||
|
this.activeSession = {
|
||||||
fileId: fileChange.id,
|
fileId: fileChange.id,
|
||||||
beforeTmpPath: beforePath,
|
beforeTmpPath: beforePath,
|
||||||
afterTmpPath: afterPath,
|
afterTmpPath: afterPath,
|
||||||
beforeUri: vscode.Uri.file(beforePath),
|
beforeUri: vscode.Uri.file(beforePath),
|
||||||
afterUri: vscode.Uri.file(afterPath),
|
afterUri: vscode.Uri.file(afterPath),
|
||||||
});
|
};
|
||||||
|
|
||||||
await vscode.commands.executeCommand(
|
await vscode.commands.executeCommand(
|
||||||
'vscode.diff',
|
'vscode.diff',
|
||||||
this.activeSession!.beforeUri,
|
this.activeSession.beforeUri,
|
||||||
this.activeSession!.afterUri,
|
this.activeSession.afterUri,
|
||||||
`${fileName} — ${fileChange.edits.length} 次 AI 编辑 · 状态栏操作`
|
`${fileName} — ${fileChange.edits.length} 次 AI 编辑 · 状态栏操作`
|
||||||
);
|
);
|
||||||
|
|
||||||
// ★ 通知外部刷新状态栏(切换到 Diff 模式显示 Accept/Reject 按钮)
|
this.isOpening = false;
|
||||||
this.onDiffAction?.();
|
this.onDiffAction?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,9 +111,7 @@ export class DiffViewer {
|
|||||||
else vscode.window.showInformationMessage('AI Diff: 当前文件无待处理变更');
|
else vscode.window.showInformationMessage('AI Diff: 当前文件无待处理变更');
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- Diff 界面操作 (Bug 1 修复: 自动推进) ----
|
// ---- Accept/Reject(Accept/Reject 后删除临时文件 + 关闭 Diff) ----
|
||||||
|
|
||||||
// ★ Accept/Reject 后删除临时文件 + 关闭 Diff
|
|
||||||
|
|
||||||
async acceptCurrentDiff(): Promise<void> {
|
async acceptCurrentDiff(): Promise<void> {
|
||||||
if (!this.activeSession) return;
|
if (!this.activeSession) return;
|
||||||
@@ -123,7 +124,7 @@ export class DiffViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vscode.window.showInformationMessage('AI Diff: 已接受 ✓');
|
vscode.window.showInformationMessage('AI Diff: 已接受 ✓');
|
||||||
this.deleteSessionFiles(); // ★ Accept 后才删
|
this.deleteSessionFiles();
|
||||||
await this.closeDiff();
|
await this.closeDiff();
|
||||||
this.onDiffAction?.();
|
this.onDiffAction?.();
|
||||||
this.advanceToNext(fileId);
|
this.advanceToNext(fileId);
|
||||||
@@ -140,7 +141,7 @@ export class DiffViewer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vscode.window.showInformationMessage('AI Diff: 已拒绝 ✗');
|
vscode.window.showInformationMessage('AI Diff: 已拒绝 ✗');
|
||||||
this.deleteSessionFiles(); // ★ Reject 后才删
|
this.deleteSessionFiles();
|
||||||
await this.closeDiff();
|
await this.closeDiff();
|
||||||
this.onDiffAction?.();
|
this.onDiffAction?.();
|
||||||
this.advanceToNext(fileId);
|
this.advanceToNext(fileId);
|
||||||
@@ -153,16 +154,15 @@ export class DiffViewer {
|
|||||||
if (ok) {
|
if (ok) {
|
||||||
vscode.window.showInformationMessage('AI Diff: 已接受此文件全部 ✓');
|
vscode.window.showInformationMessage('AI Diff: 已接受此文件全部 ✓');
|
||||||
}
|
}
|
||||||
this.deleteSessionFiles(); // ★ AcceptAll 后才删
|
this.deleteSessionFiles();
|
||||||
await this.closeDiff();
|
await this.closeDiff();
|
||||||
this.onDiffAction?.();
|
this.onDiffAction?.();
|
||||||
this.advanceToNext(fileId);
|
this.advanceToNext(fileId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- ★ Bug 1: 自动推进 ----
|
// ---- 自动推进 ----
|
||||||
|
|
||||||
private advanceToNext(afterFileId: string): void {
|
private advanceToNext(afterFileId: string): void {
|
||||||
// 如果此文件仍有待处理编辑,重新打开此文件
|
|
||||||
if (!this.changeSetManager.isFileFullyProcessed(afterFileId)) {
|
if (!this.changeSetManager.isFileFullyProcessed(afterFileId)) {
|
||||||
const set = this.changeSetManager.getCurrentSet();
|
const set = this.changeSetManager.getCurrentSet();
|
||||||
const same = set?.changes.find(c => c.id === afterFileId);
|
const same = set?.changes.find(c => c.id === afterFileId);
|
||||||
@@ -172,7 +172,6 @@ export class DiffViewer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 否则推进到下一个 pending 文件
|
|
||||||
const next = this.changeSetManager.findNextPending(afterFileId);
|
const next = this.changeSetManager.findNextPending(afterFileId);
|
||||||
if (next) {
|
if (next) {
|
||||||
setTimeout(() => this.showFileDiff(next), 150);
|
setTimeout(() => this.showFileDiff(next), 150);
|
||||||
@@ -189,7 +188,7 @@ export class DiffViewer {
|
|||||||
if (this.activeSession) {
|
if (this.activeSession) {
|
||||||
try { await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); } catch { /* ok */ }
|
try { await vscode.commands.executeCommand('workbench.action.closeActiveEditor'); } catch { /* ok */ }
|
||||||
}
|
}
|
||||||
this.clearSession();
|
this.activeSession = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- 内部 ----
|
// ---- 内部 ----
|
||||||
@@ -203,20 +202,6 @@ export class DiffViewer {
|
|||||||
return filePath;
|
return filePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
private setSession(session: DiffSession): void {
|
|
||||||
this.clearSession();
|
|
||||||
this.activeSession = session;
|
|
||||||
this.closeListener = vscode.window.onDidChangeVisibleTextEditors(() => {
|
|
||||||
if (!this.activeSession) return;
|
|
||||||
const stillOpen = vscode.window.visibleTextEditors.some(
|
|
||||||
e => e.document.uri.toString() === this.activeSession!.beforeUri.toString() ||
|
|
||||||
e.document.uri.toString() === this.activeSession!.afterUri.toString()
|
|
||||||
);
|
|
||||||
if (!stillOpen) this.clearSession();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/** ★ 删除当前会话的临时文件(只在 Accept/Reject 时调用) */
|
|
||||||
private deleteSessionFiles(): void {
|
private deleteSessionFiles(): void {
|
||||||
if (!this.activeSession) return;
|
if (!this.activeSession) return;
|
||||||
[this.activeSession.beforeTmpPath, this.activeSession.afterTmpPath].forEach(p => {
|
[this.activeSession.beforeTmpPath, this.activeSession.afterTmpPath].forEach(p => {
|
||||||
@@ -227,12 +212,6 @@ export class DiffViewer {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 清除会话状态(不删文件!手动关闭 Diff 后不影响重新打开) */
|
|
||||||
private clearSession(): void {
|
|
||||||
this.activeSession = null;
|
|
||||||
if (this.closeListener) { this.closeListener.dispose(); this.closeListener = null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanupTmpFiles(): void {
|
cleanupTmpFiles(): void {
|
||||||
for (const p of this.tmpFiles) {
|
for (const p of this.tmpFiles) {
|
||||||
try { if (fs.existsSync(p)) fs.unlinkSync(p); } catch { /* ok */ }
|
try { if (fs.existsSync(p)) fs.unlinkSync(p); } catch { /* ok */ }
|
||||||
|
|||||||
Reference in New Issue
Block a user