Skip to content

Commit c933504

Browse files
authored
fix(ui): better handle patch file support when rendering patch/edit tools (#26828)
1 parent 2d0d3d5 commit c933504

2 files changed

Lines changed: 46 additions & 18 deletions

File tree

packages/ui/src/components/message-part.tsx

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ function getDirectory(path: string | undefined) {
264264
}
265265

266266
import type { IconProps } from "./icon"
267+
import { normalize } from "./session-diff"
267268

268269
export type ToolInfo = {
269270
icon: IconProps["name"]
@@ -1878,6 +1879,31 @@ ToolRegistry.register({
18781879
const path = createMemo(() => props.metadata?.filediff?.file || props.input.filePath || "")
18791880
const filename = () => getFilename(props.input.filePath ?? "")
18801881
const pending = () => props.status === "pending" || props.status === "running"
1882+
1883+
const fileCompProps = createMemo(() => {
1884+
try {
1885+
if (props.metadata?.filediff) {
1886+
const diff = normalize({
1887+
...props.metadata?.filediff,
1888+
status: "modified",
1889+
})
1890+
const fileDiff = diff.fileDiff
1891+
if (fileDiff) return { fileDiff, hunkSeparators: fileDiff.isPartial ? "simple" : "line-info-basic" }
1892+
}
1893+
} catch {}
1894+
1895+
return {
1896+
before: {
1897+
name: props.metadata?.filediff?.file || props.input.filePath,
1898+
contents: props.metadata?.filediff?.before || props.input.oldString || "",
1899+
},
1900+
after: {
1901+
name: props.metadata?.filediff?.file || props.input.filePath,
1902+
contents: props.metadata?.filediff?.after || props.input.newString || "",
1903+
},
1904+
}
1905+
})
1906+
18811907
return (
18821908
<div data-component="edit-tool">
18831909
<BasicTool
@@ -1919,18 +1945,7 @@ ToolRegistry.register({
19191945
}
19201946
>
19211947
<div data-component="edit-content">
1922-
<Dynamic
1923-
component={fileComponent}
1924-
mode="diff"
1925-
before={{
1926-
name: props.metadata?.filediff?.file || props.input.filePath,
1927-
contents: props.metadata?.filediff?.before || props.input.oldString || "",
1928-
}}
1929-
after={{
1930-
name: props.metadata?.filediff?.file || props.input.filePath,
1931-
contents: props.metadata?.filediff?.after || props.input.newString || "",
1932-
}}
1933-
/>
1948+
<Dynamic component={fileComponent} mode="diff" {...fileCompProps()} />
19341949
</div>
19351950
</ToolFileAccordion>
19361951
</Show>
@@ -2111,7 +2126,12 @@ ToolRegistry.register({
21112126
<Accordion.Content>
21122127
<Show when={visible()}>
21132128
<div data-component="apply-patch-file-diff">
2114-
<Dynamic component={fileComponent} mode="diff" fileDiff={file.view.fileDiff} />
2129+
<Dynamic
2130+
component={fileComponent}
2131+
mode="diff"
2132+
fileDiff={file.view.fileDiff}
2133+
hunkSeparators={file.view.fileDiff.isPartial ? "simple" : "line-info-basic"}
2134+
/>
21152135
</div>
21162136
</Show>
21172137
</Accordion.Content>

packages/ui/src/components/session-diff.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { parseDiffFromFile, type FileDiffMetadata } from "@pierre/diffs"
1+
import { parseDiffFromFile, parsePatchFiles, type FileDiffMetadata } from "@pierre/diffs"
22
import { formatPatch, parsePatch, structuredPatch } from "diff"
33
import type { SnapshotFileDiff, VcsFileDiff } from "@opencode-ai/sdk/v2"
44

@@ -34,6 +34,8 @@ function patch(diff: ReviewDiff) {
3434
const afterLines: Array<{ text: string; newline: boolean }> = []
3535
let previous: "-" | "+" | " " | undefined
3636

37+
const patchIsPartial = patch.hunks.every((h) => h.oldStart > 1)
38+
3739
for (const hunk of patch.hunks) {
3840
for (const line of hunk.lines) {
3941
if (line.startsWith("\\")) {
@@ -67,9 +69,10 @@ function patch(diff: ReviewDiff) {
6769
before: beforeLines.map((line) => line.text + (line.newline ? "\n" : "")).join(""),
6870
after: afterLines.map((line) => line.text + (line.newline ? "\n" : "")).join(""),
6971
patch: diff.patch,
72+
patchIsPartial,
7073
}
7174
} catch {
72-
return { before: "", after: "", patch: diff.patch }
75+
return { before: "", after: "", patch: diff.patch, patchIsPartial: false }
7376
}
7477
}
7578
return {
@@ -86,27 +89,32 @@ function patch(diff: ReviewDiff) {
8689
{ context: Number.MAX_SAFE_INTEGER },
8790
),
8891
),
92+
patchIsPartial: false,
8993
}
9094
}
9195

92-
function file(file: string, patch: string, before: string, after: string) {
96+
function file(file: string, patch: string, before: string, after: string, partial = false) {
9397
const hit = cache.get(patch)
9498
if (hit) return hit
9599

96-
const value = parseDiffFromFile({ name: file, contents: before }, { name: file, contents: after })
100+
let value: FileDiffMetadata | undefined
101+
if (partial) value = parsePatchFiles(patch)[0]?.files[0]
102+
if (value === undefined) value = parseDiffFromFile({ name: file, contents: before }, { name: file, contents: after })
103+
97104
cache.set(patch, value)
98105
return value
99106
}
100107

101108
export function normalize(diff: ReviewDiff): ViewDiff {
102109
const next = patch(diff)
110+
const fileDiff = file(diff.file, next.patch, next.before, next.after, next.patchIsPartial)
103111
return {
104112
file: diff.file,
105113
patch: next.patch,
106114
additions: diff.additions,
107115
deletions: diff.deletions,
108116
status: diff.status,
109-
fileDiff: file(diff.file, next.patch, next.before, next.after),
117+
fileDiff,
110118
}
111119
}
112120

0 commit comments

Comments
 (0)