Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions content/solid/diff-view.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,7 @@ import { DiffView } from "@/components/ui/diff-view"
```tsx
<DiffView patch={patch} />
```

Pass a unified diff with complete hunk ranges. Compact or truncated hunks are
also supported; keep the `diff --git` separators when combining them across
multiple files so file headers can be distinguished from changed text.
37 changes: 31 additions & 6 deletions src/components/ui/diff-view.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,39 @@
import { cn } from "@/lib/utils"

/** File headers and changed text can have the same prefix, even "+++ ". */
function patchLines(patch: string): string[] {
const lines: string[] = []
let inHunk = false
let oldLines = 0
let newLines = 0
for (const line of patch.replace(/\n$/, "").split("\n")) {
if (line.startsWith("diff --git ")) inHunk = false
const hunk = /^@@ -\d+(?:,(\d+))? \+\d+(?:,(\d+))? @@/.exec(line)
if (hunk) {
oldLines = Number(hunk[1] ?? 1)
newLines = Number(hunk[2] ?? 1)
inHunk = oldLines > 0 || newLines > 0
} else if (line.startsWith("@@")) {
// Also accept abbreviated hunks, as used in compact tool output.
inHunk = true
oldLines = newLines = Infinity
} else if (!inHunk && /^(---|\+\+\+) /.test(line)) {
continue
} else if (inHunk) {
if (line.startsWith("-") || line.startsWith(" ")) oldLines--
if (line.startsWith("+") || line.startsWith(" ")) newLines--
if (oldLines <= 0 && newLines <= 0) inHunk = false
}
lines.push(line)
}
return lines
}

/** Lines added and removed by a unified patch, headers aside. */
function patchStats(patch: string): { added: number; removed: number } {
let added = 0
let removed = 0
for (const line of patch.split("\n")) {
if (line.startsWith("+++") || line.startsWith("---")) continue
for (const line of patchLines(patch)) {
if (line.startsWith("+")) added++
else if (line.startsWith("-")) removed++
}
Expand All @@ -23,10 +51,7 @@ function DiffStats({ added, removed, className }: { added: number; removed: numb

/** A patch as a diff: hunks with added and removed lines coloured, headers dropped. */
function DiffView({ patch, className }: { patch: string; className?: string }) {
const lines = patch
.replace(/\n$/, "")
.split("\n")
.filter((line) => !line.startsWith("+++") && !line.startsWith("---"))
const lines = patchLines(patch)
return (
<div
data-slot="diff-view"
Expand Down
Loading