fix(core): compose successive patch updates (#38034)
This commit is contained in:
parent
a81c04fd31
commit
eb4ff91c2d
2 changed files with 46 additions and 22 deletions
|
|
@ -111,6 +111,7 @@ export const Plugin = {
|
||||||
}
|
}
|
||||||
const prepared: Prepared[] = []
|
const prepared: Prepared[] = []
|
||||||
const targets: Target[] = []
|
const targets: Target[] = []
|
||||||
|
const updates = new Map<string, string>()
|
||||||
for (const hunk of hunks) {
|
for (const hunk of hunks) {
|
||||||
yield* Effect.gen(function* () {
|
yield* Effect.gen(function* () {
|
||||||
const target = resolveTarget(location, hunk.path)
|
const target = resolveTarget(location, hunk.path)
|
||||||
|
|
@ -154,28 +155,34 @@ export const Plugin = {
|
||||||
prepared.push({ ...hunk, target, before: original.replace(/^\uFEFF/, ""), after: "" })
|
prepared.push({ ...hunk, target, before: original.replace(/^\uFEFF/, ""), after: "" })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const stats = yield* fs.stat(target.canonical).pipe(
|
const previous = updates.get(target.canonical)
|
||||||
Effect.mapError(
|
const original =
|
||||||
(error) =>
|
previous ??
|
||||||
new ToolFailure({
|
(yield* Effect.gen(function* () {
|
||||||
message: `patch verification failed: Failed to read file to update ${target.canonical}: ${error instanceof Error ? error.message : String(error)}`,
|
const stats = yield* fs.stat(target.canonical).pipe(
|
||||||
}),
|
Effect.mapError(
|
||||||
),
|
(error) =>
|
||||||
)
|
new ToolFailure({
|
||||||
if (stats.type === "Directory") {
|
message: `patch verification failed: Failed to read file to update ${target.canonical}: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
return yield* new ToolFailure({
|
}),
|
||||||
message: `patch verification failed: Failed to read file to update ${target.canonical}: path is a directory`,
|
),
|
||||||
})
|
)
|
||||||
}
|
if (stats.type === "Directory") {
|
||||||
const content = yield* fs.readFile(target.canonical).pipe(
|
return yield* new ToolFailure({
|
||||||
Effect.mapError(
|
message: `patch verification failed: Failed to read file to update ${target.canonical}: path is a directory`,
|
||||||
(error) =>
|
})
|
||||||
new ToolFailure({
|
}
|
||||||
message: `patch verification failed: Failed to read file to update ${target.canonical}: ${error instanceof Error ? error.message : String(error)}`,
|
return new TextDecoder("utf-8", { ignoreBOM: true }).decode(
|
||||||
}),
|
yield* fs.readFile(target.canonical).pipe(
|
||||||
),
|
Effect.mapError(
|
||||||
)
|
(error) =>
|
||||||
const original = new TextDecoder("utf-8", { ignoreBOM: true }).decode(content)
|
new ToolFailure({
|
||||||
|
message: `patch verification failed: Failed to read file to update ${target.canonical}: ${error instanceof Error ? error.message : String(error)}`,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}))
|
||||||
const before = original.replace(/^\uFEFF/, "")
|
const before = original.replace(/^\uFEFF/, "")
|
||||||
const update = yield* Effect.try({
|
const update = yield* Effect.try({
|
||||||
try: () => Patch.derive(hunk.path, hunk.chunks, original),
|
try: () => Patch.derive(hunk.path, hunk.chunks, original),
|
||||||
|
|
@ -205,6 +212,7 @@ export const Plugin = {
|
||||||
after: update.content,
|
after: update.content,
|
||||||
moveTarget,
|
moveTarget,
|
||||||
})
|
})
|
||||||
|
if (!moveTarget) updates.set(target.canonical, Patch.joinBom(update.content, update.bom))
|
||||||
}).pipe(Effect.mapError((error) => (error instanceof ToolFailure ? error : fail(hunk.path, error))))
|
}).pipe(Effect.mapError((error) => (error instanceof ToolFailure ? error : fail(hunk.path, error))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -487,6 +487,22 @@ describe("PatchTool", () => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
it.live("applies successive update operations to one file", () =>
|
||||||
|
withTempTool((directory, registry) =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const target = path.join(directory, "successive.txt")
|
||||||
|
yield* Effect.promise(() => fs.writeFile(target, "a\nb\n"))
|
||||||
|
yield* executeTool(
|
||||||
|
registry,
|
||||||
|
call(
|
||||||
|
"*** Begin Patch\n*** Update File: successive.txt\n@@\n-a\n+A\n*** Update File: successive.txt\n@@\n-b\n+B\n*** End Patch",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
expect(yield* Effect.promise(() => fs.readFile(target, "utf8"))).toBe("A\nB\n")
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
it.live("does not invent a first-line diff for BOM files", () =>
|
it.live("does not invent a first-line diff for BOM files", () =>
|
||||||
withTempTool((directory, registry) =>
|
withTempTool((directory, registry) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue