Skip to content

Commit bb1f88f

Browse files
committed
Improve diff processor logic for edge cases and add regression tests
1 parent 7a767fa commit bb1f88f

11 files changed

Lines changed: 106 additions & 14 deletions

File tree

apps/editor/src/commands/apply-chat-response-command/utils/edit-formats/diffs/diff-processor.spec.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,5 +180,47 @@ describe('diff-processor', () => {
180180

181181
expect(result).toBe(expected)
182182
})
183+
184+
it('applies diff with generic test case 8 correctly', async () => {
185+
const test_case = 'generic-8'
186+
const original = load_test_case_file('', test_case, 'original.txt')
187+
const diff = load_test_case_file('', test_case, 'diff.txt')
188+
const expected = load_test_case_file('', test_case, 'expected.txt')
189+
190+
const result = apply_diff({
191+
original_code: original,
192+
diff_patch: diff
193+
})
194+
195+
expect(result).toBe(expected)
196+
})
197+
198+
it('applies diff replacing a line with the exact same line', async () => {
199+
const test_case = 'same-line-replace'
200+
const original = load_test_case_file('', test_case, 'original.txt')
201+
const diff = load_test_case_file('', test_case, 'diff.txt')
202+
const expected = load_test_case_file('', test_case, 'expected.txt')
203+
204+
const result = apply_diff({
205+
original_code: original,
206+
diff_patch: diff
207+
})
208+
209+
expect(result).toBe(expected)
210+
})
211+
212+
it('applies diff to an empty file correctly', async () => {
213+
const test_case = 'empty-file'
214+
const original = load_test_case_file('', test_case, 'original.txt')
215+
const diff = load_test_case_file('', test_case, 'diff.txt')
216+
const expected = load_test_case_file('', test_case, 'expected.txt')
217+
218+
const result = apply_diff({
219+
original_code: original,
220+
diff_patch: diff
221+
})
222+
223+
expect(result).toBe(expected)
224+
})
183225
})
184226
})

apps/editor/src/commands/apply-chat-response-command/utils/edit-formats/diffs/diff-processor.ts

Lines changed: 32 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -233,21 +233,21 @@ export const apply_diff = (params: {
233233

234234
let max_trim_top = 0
235235
for (let k = 0; k < safe_trim_indices.length; k++) {
236-
if (safe_trim_indices[k] === k) {
236+
if (safe_trim_indices[k] == k) {
237237
max_trim_top = k + 1
238238
} else {
239239
break
240240
}
241241
}
242242

243-
if (max_trim_top > 0 && safe_trim_indices.length === max_trim_top) {
243+
if (max_trim_top > 0 && safe_trim_indices.length == max_trim_top) {
244244
if (
245245
max_trim_top < block.replace_lines.length &&
246246
block.replace_lines[max_trim_top].search_index === null
247247
) {
248248
let has_deletion = true
249249
for (let r = max_trim_top + 1; r < block.replace_lines.length; r++) {
250-
if (block.replace_lines[r].search_index === max_trim_top) {
250+
if (block.replace_lines[r].search_index == max_trim_top) {
251251
has_deletion = false
252252
break
253253
}
@@ -268,7 +268,7 @@ export const apply_diff = (params: {
268268

269269
for (let trim_cnt = 0; trim_cnt <= max_trim_top; trim_cnt++) {
270270
const current_search_lines = block.search_lines.slice(trim_cnt)
271-
if (current_search_lines.length === 0) continue
271+
if (current_search_lines.length == 0) continue
272272

273273
for (
274274
let j = previous_found_index;
@@ -342,7 +342,11 @@ export const apply_diff = (params: {
342342
const has_additions = block.replace_lines.some(
343343
(l) => l.search_index === null
344344
)
345-
if (has_additions && block.after_search_lines.length > 0) {
345+
const is_noop =
346+
block.search_lines.length == block.after_search_lines.length &&
347+
block.search_lines.every((v, i) => v == block.after_search_lines[i])
348+
349+
if (has_additions && block.after_search_lines.length > 0 && !is_noop) {
346350
const check_start = Math.max(
347351
0,
348352
matched_info.start_index - block.after_search_lines.length
@@ -364,12 +368,12 @@ export const apply_diff = (params: {
364368
const a_val = block.after_search_lines[after_ptr]
365369
const o_val = original_code_lines_normalized[orig_ptr].value
366370

367-
if (a_val === o_val) {
371+
if (a_val == o_val) {
368372
after_ptr++
369373
orig_ptr++
370-
} else if (o_val === '~nnn') {
374+
} else if (o_val == '~nnn') {
371375
orig_ptr++
372-
} else if (a_val === '~nnn') {
376+
} else if (a_val == '~nnn') {
373377
after_ptr++
374378
} else {
375379
break
@@ -423,7 +427,14 @@ export const apply_diff = (params: {
423427
block.search_to_original_map.values()
424428
)
425429

426-
for (const line of block.replace_lines) {
430+
const search_count =
431+
block.actual_original_line_count || block.search_lines.length
432+
const end_original_idx = start_index + search_count - 1
433+
434+
for (let i = 0; i < block.replace_lines.length; i++) {
435+
const line = block.replace_lines[i]
436+
const is_last_replace_line = i === block.replace_lines.length - 1
437+
427438
if (line.search_index != null) {
428439
const original_idx = block.search_to_original_map.get(line.search_index)
429440
if (original_idx != undefined) {
@@ -438,21 +449,28 @@ export const apply_diff = (params: {
438449
let content = original_code_lines[original_idx]
439450
if (
440451
!content.endsWith('\n') &&
441-
original_idx < original_code_lines.length - 1
452+
(original_idx < original_code_lines.length - 1 ||
453+
!is_last_replace_line)
442454
) {
443455
content += '\n'
444456
}
445457
replacement_content.push(content)
446458
last_original_idx = original_idx
447459
}
448460
} else {
449-
replacement_content.push(line.content)
461+
let content = line.content
462+
if (
463+
is_last_replace_line &&
464+
original_code_lines.length > 0 &&
465+
end_original_idx === original_code_lines.length - 1 &&
466+
!original_code_lines[original_code_lines.length - 1].endsWith('\n')
467+
) {
468+
content = content.replace(/\n$/, '')
469+
}
470+
replacement_content.push(content)
450471
}
451472
}
452473

453-
const search_count =
454-
block.actual_original_line_count || block.search_lines.length
455-
const end_original_idx = start_index + search_count - 1
456474
for (let skip = last_original_idx + 1; skip <= end_original_idx; skip++) {
457475
if (
458476
original_code_lines_normalized[skip].value == '~nnn' &&
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
--- a/test.ts
2+
+++ b/test.ts
3+
@@ -12,3 +12,5 @@
4+
+test
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
test

apps/editor/src/commands/apply-chat-response-command/utils/edit-formats/diffs/test-cases/empty-file/original.txt

Whitespace-only changes.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
--- a/apps/tour/.env-sample
2+
+++ b/apps/tour/.env-sample
3+
@@ -12,3 +12,5 @@
4+
5+
# TTS
6+
GEMINI_API_KEY=
7+
+GEMINI_TTS_MODEL=gemini-2.5-pro-preview-tts
8+
+GEMINI_TTS_VOICE=Aoede
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# TTS
2+
GEMINI_API_KEY=
3+
GEMINI_TTS_MODEL=gemini-2.5-pro-preview-tts
4+
GEMINI_TTS_VOICE=Aoede
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# TTS
2+
GEMINI_API_KEY=
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
--- a/file.ts
2+
+++ b/file.ts
3+
@@ -1,3 +1,3 @@
4+
function hello() {
5+
- console.log('world')
6+
+ console.log('world')
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
function hello() {
2+
console.log('world')
3+
}

0 commit comments

Comments
 (0)