Skip to content

Commit 2602094

Browse files
fix(files): parse multi-file and copy git diffs, cap word diffs, readable pie labels
1 parent 9735ca8 commit 2602094

4 files changed

Lines changed: 39 additions & 12 deletions

File tree

‎apps/sim/app/workspace/[workspaceId]/files/components/file-viewer/mermaid-theme.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,9 +57,9 @@ export function readMermaidThemeVariables(
5757
loopTextColor: body,
5858
sequenceNumberColor: background,
5959
pie1: body,
60-
pie2: token('--text-subtle'),
61-
pie3: token('--surface-7'),
62-
pie4: line,
60+
pie2: line,
61+
pie3: token('--text-subtle'),
62+
pie4: token('--text-muted'),
6363
pieStrokeColor: background,
6464
pieTitleTextColor: text,
6565
pieSectionTextColor: background,

‎apps/sim/components/diff/diff-view.tsx‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import type { DiffHunk, DiffLine } from '@/lib/diff/unified'
99
const CONTEXT_EDGE = 3
1010
/** A run of unchanged lines collapses once it is longer than this. */
1111
const COLLAPSE_AFTER = CONTEXT_EDGE * 2 + 2
12+
/** Word-level comparison is skipped past this many characters, so huge lines stay cheap. */
13+
const WORD_DIFF_MAX_CHARS = 1000
1214

1315
const ADD_ROW =
1416
'bg-[color-mix(in_srgb,var(--badge-success-bg)_35%,transparent)] text-[var(--text-primary)]'
@@ -53,6 +55,7 @@ function wordSegments(lines: DiffLine[]): Map<DiffLine, Segment[]> {
5355
const adds: DiffLine[] = []
5456
while (lines[index]?.type === 'add') adds.push(lines[index++])
5557
for (let pair = 0; pair < Math.min(dels.length, adds.length); pair++) {
58+
if (dels[pair].text.length + adds[pair].text.length > WORD_DIFF_MAX_CHARS) continue
5659
const parts = diffWordsWithSpace(dels[pair].text, adds[pair].text)
5760
segments.set(
5861
dels[pair],
@@ -158,9 +161,10 @@ export function DiffView({ hunks }: DiffViewProps) {
158161
const expand = () => setExpanded((current) => new Set(current).add(hunkIndex))
159162
return (
160163
<Fragment key={hunkIndex}>
161-
{(hunk.heading || hunkIndex > 0) && (
164+
{(hunk.file || hunk.heading || hunkIndex > 0) && (
162165
<div className='sticky left-0 col-span-full px-3 pt-2 pb-1 font-sans text-[var(--text-muted)] text-caption'>
163-
{hunk.heading || '⋯'}
166+
{hunk.file && <span className='text-[var(--text-body)]'>{hunk.file} </span>}
167+
{hunk.heading || (hunk.file ? '' : '⋯')}
164168
</div>
165169
)}
166170
{items.map((item, index) =>

‎apps/sim/lib/diff/unified.test.ts‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,17 @@ describe('parseUnifiedDiff', () => {
4747
})
4848
})
4949

50+
it('splits a multi-file git diff into hunks per file', () => {
51+
const diff = parseUnifiedDiff(
52+
'diff --git a/a.ts b/a.ts\n--- a/a.ts\n+++ b/a.ts\n@@ -1,1 +1,1 @@\n-one\n+uno\ndiff --git a/b.ts b/c.ts\nsimilarity index 90%\ncopy from b.ts\ncopy to c.ts\n--- a/b.ts\n+++ b/c.ts\n@@ -3,1 +3,1 @@\n-two\n+dos'
53+
)
54+
expect(diff.path).toBe('2 files')
55+
expect(diff.hunks.map((hunk) => [hunk.file, hunk.lines.map((line) => line.text)])).toEqual([
56+
['a.ts', ['one', 'uno']],
57+
['c.ts', ['two', 'dos']],
58+
])
59+
})
60+
5061
it('names each side of a two-document comparison', () => {
5162
const diff = parseUnifiedDiff(
5263
'--- sim:knowledge/kb/old\n+++ sim:knowledge/kb/new\n-Refunds are available within 14 days.\n+Annual plans can be refunded within 30 days.'

‎apps/sim/lib/diff/unified.ts‎

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ export interface DiffLine {
1414
export interface DiffHunk {
1515
/** Free text after the `@@ … @@` range, often the enclosing heading or function. */
1616
heading: string
17+
/** The file a hunk belongs to, set only when a git diff spans several files. */
18+
file?: string
1719
lines: DiffLine[]
1820
}
1921

@@ -29,7 +31,7 @@ export interface UnifiedDiff {
2931

3032
/** Git metadata that precedes `---`/`+++` in `git diff` output and carries nothing to render. */
3133
const GIT_HEADER =
32-
/^(?:diff --git |index |new file mode |deleted file mode |similarity |rename |old mode |new mode |Binary files )/
34+
/^(?:index |new file mode |deleted file mode |similarity |dissimilarity |rename |copy |old mode |new mode |Binary files )/
3335
const HUNK_HEADER = /^@@(?: -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@)?\s?(.*?)(?:\s*@@)?$/
3436
const SIM_SOURCE = /^sim:(file|knowledge)\/([^/\s]+)(?:\/([^/\s]+))?$/
3537

@@ -72,28 +74,35 @@ export function parseUnifiedDiff(text: string): UnifiedDiff {
7274
let oldLine: number | undefined
7375
let newLine: number | undefined
7476

77+
let file: string | null = null
7578
for (const [index, row] of rows.entries()) {
79+
if (row.startsWith('diff --git ')) {
80+
hunk = null
81+
file = null
82+
continue
83+
}
7684
if (!hunk && (row.startsWith('--- ') || row.startsWith('+++ '))) {
7785
const target = row.slice(4).trim()
7886
const parsed = parseSource(target)
79-
if (row.startsWith('--- ')) oldSource = parsed
80-
else newSource = parsed
81-
if (!parsed && target !== '/dev/null' && (row.startsWith('+++ ') || !path))
82-
path = target.replace(/^[ab]\//, '').split('\t')[0]
87+
if (row.startsWith('--- ')) oldSource = parsed ?? oldSource
88+
else newSource = parsed ?? newSource
89+
if (!parsed && target !== '/dev/null' && (row.startsWith('+++ ') || !file))
90+
file = target.replace(/^[ab]\//, '').split('\t')[0]
91+
path ??= file
8392
continue
8493
}
8594
if (!hunk && GIT_HEADER.test(row)) continue
8695
const header = HUNK_HEADER.exec(row)
8796
if (row.startsWith('@@') && header) {
88-
hunk = { heading: header[3] ?? '', lines: [] }
97+
hunk = { heading: header[3] ?? '', file: file ?? undefined, lines: [] }
8998
hunks.push(hunk)
9099
oldLine = header[1] ? Number(header[1]) : undefined
91100
newLine = header[2] ? Number(header[2]) : undefined
92101
continue
93102
}
94103
if (row.startsWith('\\')) continue
95104
if (!hunk) {
96-
hunk = { heading: '', lines: [] }
105+
hunk = { heading: '', file: file ?? undefined, lines: [] }
97106
hunks.push(hunk)
98107
}
99108
const marker = row[0]
@@ -114,5 +123,8 @@ export function parseUnifiedDiff(text: string): UnifiedDiff {
114123
throw new Error('A diff needs at least one + or - line')
115124
oldSource ??= newSource
116125
newSource ??= oldSource
126+
const files = new Set(hunks.map((entry) => entry.file))
127+
if (files.size <= 1) for (const entry of hunks) entry.file = undefined
128+
else path = `${files.size} files`
117129
return { oldSource, newSource, path: oldSource ? null : path, hunks }
118130
}

0 commit comments

Comments
 (0)