|
@@ -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 CodeBlock from "./CodeBlock"
|
|
|
import styles from "./diffview.module.css"
|
|
import styles from "./diffview.module.css"
|
|
|
|
|
|
|
|
type DiffRow = {
|
|
type DiffRow = {
|
|
|
left: string
|
|
left: string
|
|
|
right: string
|
|
right: string
|
|
|
- type: "added" | "removed" | "unchanged"
|
|
|
|
|
|
|
+ type: "added" | "removed" | "unchanged" | "modified"
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
interface DiffViewProps {
|
|
interface DiffViewProps {
|
|
|
- changes: ChangeObject<string>[]
|
|
|
|
|
|
|
+ diff: string
|
|
|
lang?: string
|
|
lang?: string
|
|
|
class?: string
|
|
class?: string
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const DiffView: Component<DiffViewProps> = (props) => {
|
|
const DiffView: Component<DiffViewProps> = (props) => {
|
|
|
|
|
+
|
|
|
const rows = createMemo(() => {
|
|
const rows = createMemo(() => {
|
|
|
const diffRows: DiffRow[] = []
|
|
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
|
|
return diffRows
|
|
@@ -43,7 +119,7 @@ const DiffView: Component<DiffViewProps> = (props) => {
|
|
|
code={r.left}
|
|
code={r.left}
|
|
|
lang={props.lang}
|
|
lang={props.lang}
|
|
|
data-section="cell"
|
|
data-section="cell"
|
|
|
- data-diff-type={r.type === "removed" ? "removed" : ""}
|
|
|
|
|
|
|
+ data-diff-type={r.type === "removed" || r.type === "modified" ? "removed" : ""}
|
|
|
/>
|
|
/>
|
|
|
))}
|
|
))}
|
|
|
</div>
|
|
</div>
|
|
@@ -54,7 +130,7 @@ const DiffView: Component<DiffViewProps> = (props) => {
|
|
|
code={r.right}
|
|
code={r.right}
|
|
|
lang={props.lang}
|
|
lang={props.lang}
|
|
|
data-section="cell"
|
|
data-section="cell"
|
|
|
- data-diff-type={r.type === "added" ? "added" : ""}
|
|
|
|
|
|
|
+ data-diff-type={r.type === "added" || r.type === "modified" ? "added" : ""}
|
|
|
/>
|
|
/>
|
|
|
))}
|
|
))}
|
|
|
</div>
|
|
</div>
|