Dax Raad 8 mesi fa
parent
commit
f76cdfff9b

+ 1 - 3
packages/opencode/src/tool/edit.ts

@@ -3,7 +3,7 @@ import * as path from "path"
 import { Tool } from "./tool"
 import { FileTimes } from "./util/file-times"
 import { LSP } from "../lsp"
-import { createTwoFilesPatch, diffLines } from "diff"
+import { createTwoFilesPatch } from "diff"
 import { Permission } from "../permission"
 import DESCRIPTION from "./edit.txt"
 
@@ -85,7 +85,6 @@ export const EditTool = Tool.define({
       await file.write(contentNew)
     })()
 
-    const changes = diffLines(contentOld, contentNew)
     const diff = createTwoFilesPatch(filepath, filepath, contentOld, contentNew)
 
     FileTimes.read(ctx.sessionID, filepath)
@@ -105,7 +104,6 @@ export const EditTool = Tool.define({
     return {
       metadata: {
         diagnostics,
-        changes,
         diff,
       },
       output,

+ 91 - 15
packages/web/src/components/DiffView.tsx

@@ -1,35 +1,111 @@
-import { type Component, createMemo, createSignal, onMount } from "solid-js"
-import { diffLines, type ChangeObject } from "diff"
+import { type Component, createMemo } from "solid-js"
+import { parsePatch } from "diff"
 import CodeBlock from "./CodeBlock"
 import styles from "./diffview.module.css"
 
 type DiffRow = {
   left: string
   right: string
-  type: "added" | "removed" | "unchanged"
+  type: "added" | "removed" | "unchanged" | "modified"
 }
 
 interface DiffViewProps {
-  changes: ChangeObject<string>[]
+  diff: string
   lang?: string
   class?: string
 }
 
 const DiffView: Component<DiffViewProps> = (props) => {
+
   const rows = createMemo(() => {
     const diffRows: DiffRow[] = []
 
-    for (const item of props.changes) {
-      const lines = item.value.split(/\r?\n/)
-      if (lines.at(-1) === "") lines.pop()
+    try {
+      const patches = parsePatch(props.diff)
+
+      for (const patch of patches) {
+        for (const hunk of patch.hunks) {
+          const lines = hunk.lines
+          let i = 0
+
+          while (i < lines.length) {
+            const line = lines[i]
+            const content = line.slice(1)
+            const prefix = line[0]
+
+            if (prefix === '-') {
+              // Look ahead for consecutive additions to pair with removals
+              const removals: string[] = [content]
+              let j = i + 1
+
+              // Collect all consecutive removals
+              while (j < lines.length && lines[j][0] === '-') {
+                removals.push(lines[j].slice(1))
+                j++
+              }
+
+              // Collect all consecutive additions that follow
+              const additions: string[] = []
+              while (j < lines.length && lines[j][0] === '+') {
+                additions.push(lines[j].slice(1))
+                j++
+              }
+
+              // Pair removals with additions
+              const maxLength = Math.max(removals.length, additions.length)
+              for (let k = 0; k < maxLength; k++) {
+                const hasLeft = !!removals[k]
+                const hasRight = !!additions[k]
+
+                if (hasLeft && hasRight) {
+                  // Replacement - left is removed, right is added
+                  diffRows.push({
+                    left: removals[k],
+                    right: additions[k],
+                    type: "modified"
+                  })
+                } else if (hasLeft) {
+                  // Pure removal
+                  diffRows.push({
+                    left: removals[k],
+                    right: "",
+                    type: "removed"
+                  })
+                } else {
+                  // Pure addition
+                  diffRows.push({
+                    left: "",
+                    right: additions[k],
+                    type: "added"
+                  })
+                }
+              }
 
-      for (const line of lines) {
-        diffRows.push({
-          left: item.removed ? line : item.added ? "" : line,
-          right: item.added ? line : item.removed ? "" : line,
-          type: item.added ? "added" : item.removed ? "removed" : "unchanged",
-        })
+              i = j
+            } else if (prefix === '+') {
+              // Standalone addition (not paired with removal)
+              diffRows.push({
+                left: "",
+                right: content,
+                type: "added"
+              })
+              i++
+            } else if (prefix === ' ') {
+              diffRows.push({
+                left: content,
+                right: content,
+                type: "unchanged"
+              })
+              i++
+            } else {
+              i++
+            }
+          }
+        }
       }
+    } catch (error) {
+      console.error("Failed to parse patch:", error)
+      return []
     }
 
     return diffRows
@@ -43,7 +119,7 @@ const DiffView: Component<DiffViewProps> = (props) => {
             code={r.left}
             lang={props.lang}
             data-section="cell"
-            data-diff-type={r.type === "removed" ? "removed" : ""}
+            data-diff-type={r.type === "removed" || r.type === "modified" ? "removed" : ""}
           />
         ))}
       </div>
@@ -54,7 +130,7 @@ const DiffView: Component<DiffViewProps> = (props) => {
             code={r.right}
             lang={props.lang}
             data-section="cell"
-            data-diff-type={r.type === "added" ? "added" : ""}
+            data-diff-type={r.type === "added" || r.type === "modified" ? "added" : ""}
           />
         ))}
       </div>

+ 1 - 1
packages/web/src/components/Share.tsx

@@ -766,7 +766,7 @@ export default function Share(props: { api: string }) {
                                     <div data-part-tool-edit>
                                       <DiffView
                                         class={styles["diff-code-block"]}
-                                        changes={metadata()?.changes || []}
+                                        diff={metadata()?.diff}
                                         lang={getFileType(filePath)}
                                       />
                                     </div>