Skip to content

Commit ffcfe05

Browse files
committed
feat(file-safety): atomic text publish primitive + safeWriteJson refactor (A4, #1375)
1 parent 78c712a commit ffcfe05

5 files changed

Lines changed: 547 additions & 40 deletions

File tree

src/integrations/editor/DiffViewProvider.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { arePathsEqual, getReadablePath } from "../../utils/path"
1818
import { formatResponse } from "../../core/prompts/responses"
1919
import { diagnosticsToProblemsString, getNewDiagnostics } from "../diagnostics"
2020
import { Task } from "../../core/task/Task"
21+
import { safeWriteText } from "../../services/file-safety/safeWriteText"
2122

2223
import { DecorationController } from "./DecorationController"
2324

@@ -1156,7 +1157,7 @@ export class DiffViewProvider {
11561157

11571158
// Write the content directly to the file
11581159
await createDirectoriesForFile(absolutePath)
1159-
await fs.writeFile(absolutePath, content, "utf-8")
1160+
await safeWriteText(absolutePath, content)
11601161

11611162
// Open the document to ensure diagnostics are loaded
11621163
// When openFile is false (PREVENT_FOCUS_DISRUPTION enabled), we only open in memory

src/integrations/editor/__tests__/DiffViewProvider.spec.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,14 @@ vi.mock("fs/promises", () => ({
1515
readFile: vi.fn().mockResolvedValue("file content"),
1616
writeFile: vi.fn().mockResolvedValue(undefined),
1717
access: vi.fn().mockResolvedValue(undefined),
18+
mkdir: vi.fn().mockResolvedValue(undefined),
19+
rename: vi.fn().mockResolvedValue(undefined),
20+
unlink: vi.fn().mockResolvedValue(undefined),
21+
}))
22+
23+
// Mock safeWriteText (used by saveDirectly)
24+
vi.mock("../../../services/file-safety/safeWriteText", () => ({
25+
safeWriteText: vi.fn().mockResolvedValue(undefined),
1826
}))
1927

2028
// Mock utils
@@ -26,6 +34,8 @@ vi.mock("../../../utils/fs", () => ({
2634
vi.mock("path", () => ({
2735
resolve: vi.fn((cwd, relPath) => `${cwd}/${relPath}`),
2836
basename: vi.fn((path) => path.split("/").pop()),
37+
dirname: vi.fn((path) => path.split("/").slice(0, -1).join("/") || "/"),
38+
join: (...args: string[]) => args.join("/"),
2939
}))
3040

3141
// Mock vscode
@@ -121,6 +131,11 @@ vi.mock("vscode", () => ({
121131
},
122132
}))
123133

134+
// Mock safeWriteText
135+
vi.mock("../../services/file-safety/safeWriteText", () => ({
136+
safeWriteText: vi.fn().mockResolvedValue(undefined),
137+
}))
138+
124139
// Mock DecorationController
125140
vi.mock("../DecorationController", () => ({
126141
DecorationController: vi.fn().mockImplementation(function () {
@@ -791,9 +806,9 @@ describe("DiffViewProvider", () => {
791806

792807
const result = await diffViewProvider.saveDirectly("test.ts", "new content", true, true, 2000)
793808

794-
// Verify file was written
795-
const fs = await import("fs/promises")
796-
expect(fs.writeFile).toHaveBeenCalledWith(`${mockCwd}/test.ts`, "new content", "utf-8")
809+
// Verify file was written via safeWriteText
810+
const { safeWriteText } = await import("../../../services/file-safety/safeWriteText")
811+
expect(safeWriteText).toHaveBeenCalledWith(`${mockCwd}/test.ts`, "new content")
797812

798813
// Verify file was opened without focus
799814
expect(vscode.window.showTextDocument).toHaveBeenCalledWith(
@@ -814,9 +829,9 @@ describe("DiffViewProvider", () => {
814829
it("should not open file when openWithoutFocus is false", async () => {
815830
await diffViewProvider.saveDirectly("test.ts", "new content", false, true, 1000)
816831

817-
// Verify file was written
818-
const fs = await import("fs/promises")
819-
expect(fs.writeFile).toHaveBeenCalledWith(`${mockCwd}/test.ts`, "new content", "utf-8")
832+
// Verify file was written via safeWriteText
833+
const { safeWriteText } = await import("../../../services/file-safety/safeWriteText")
834+
expect(safeWriteText).toHaveBeenCalledWith(`${mockCwd}/test.ts`, "new content")
820835

821836
// Verify file was NOT opened
822837
expect(vscode.window.showTextDocument).not.toHaveBeenCalled()
@@ -829,9 +844,9 @@ describe("DiffViewProvider", () => {
829844

830845
await diffViewProvider.saveDirectly("test.ts", "new content", true, false, 1000)
831846

832-
// Verify file was written
833-
const fs = await import("fs/promises")
834-
expect(fs.writeFile).toHaveBeenCalledWith(`${mockCwd}/test.ts`, "new content", "utf-8")
847+
// Verify file was written via safeWriteText
848+
const { safeWriteText } = await import("../../../services/file-safety/safeWriteText")
849+
expect(safeWriteText).toHaveBeenCalledWith(`${mockCwd}/test.ts`, "new content")
835850

836851
// Verify delay was NOT called
837852
expect(mockDelay).not.toHaveBeenCalled()

0 commit comments

Comments
 (0)