Skip to content

Commit 2e25da3

Browse files
committed
Refactor and consolidate edit format utilities into a unified directory structure
1 parent b933793 commit 2e25da3

64 files changed

Lines changed: 392 additions & 324 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎apps/editor/src/commands/apply-chat-response-command/handlers/conflict-markers-handler.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,8 @@ import { create_safe_path, sanitize_file_name } from '@/utils/path-sanitizer'
77
import { FileItem } from '../utils/clipboard-parser'
88
import { OriginalFileState } from '../types/original-file-state'
99
import { remove_directory_if_empty } from '../utils/file-operations'
10-
import {
11-
apply_conflict_markers_to_content,
12-
parse_conflict_segments
13-
} from '../utils/conflict-marker-parser'
10+
import { apply_conflict_markers_to_content } from '../utils/edit-formats/conflict-markers/apply-conflict-markers-to-content'
11+
import { parse_conflict_segments } from '../utils/edit-formats/conflict-markers'
1412

1513
export const handle_conflict_markers = async (
1614
files: FileItem[]

‎apps/editor/src/commands/apply-chat-response-command/handlers/diff-handler.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { Logger } from '@shared/utils/logger'
66
import { promisify } from 'util'
77
import { OriginalFileState } from '../types/original-file-state'
88
import { create_safe_path } from '@/utils/path-sanitizer'
9-
import { apply_diff } from '../utils/diff-processor'
9+
import { apply_diff } from '../utils/edit-formats/diffs'
1010
import { remove_directory_if_empty } from '../utils/file-operations'
1111

1212
const execAsync = promisify(exec)

‎apps/editor/src/commands/apply-chat-response-command/handlers/truncated-handler.ts‎

Lines changed: 1 addition & 166 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,7 @@ import { dictionary } from '@shared/constants/dictionary'
66
import { create_safe_path, sanitize_file_name } from '@/utils/path-sanitizer'
77
import { FileItem } from '../utils/clipboard-parser'
88
import { OriginalFileState } from '../types/original-file-state'
9-
10-
/*
11-
* Matches lines like:
12-
* // ...
13-
* # ...
14-
* <!-- ...
15-
* ; ...
16-
* " ...
17-
* ' ...
18-
* {/* ...
19-
* /* ...
20-
*/
21-
const TRUNCATION_REGEX = /^\s*(\/\/|#|<!--|;|"|'|\{\/\*|\/\*)\s*\.{3,}.*\s*$/
22-
23-
export const is_truncation_line = (line: string) => TRUNCATION_REGEX.test(line)
9+
import { process_truncated_content } from '../utils/edit-formats/truncations'
2410

2511
export const handle_truncated_edit = async (
2612
files: FileItem[]
@@ -126,154 +112,3 @@ export const handle_truncated_edit = async (
126112

127113
return { success: true, original_states, failed_files }
128114
}
129-
130-
const process_truncated_content = (
131-
new_text: string,
132-
original_text: string
133-
): string => {
134-
const new_lines = new_text.split(/\r?\n/)
135-
const original_lines = original_text.split(/\r?\n/)
136-
137-
const blocks: { type: 'code' | 'truncation'; lines: string[] }[] = []
138-
let buffer: string[] = []
139-
140-
for (const line of new_lines) {
141-
if (is_truncation_line(line)) {
142-
if (buffer.length > 0) {
143-
blocks.push({ type: 'code', lines: buffer })
144-
buffer = []
145-
}
146-
blocks.push({ type: 'truncation', lines: [line] })
147-
} else {
148-
buffer.push(line)
149-
}
150-
}
151-
if (buffer.length > 0) {
152-
blocks.push({ type: 'code', lines: buffer })
153-
}
154-
155-
const output_lines: string[] = []
156-
let current_original_idx = 0 // Line index in original
157-
158-
for (let i = 0; i < blocks.length; i++) {
159-
const block = blocks[i]
160-
161-
if (block.type == 'code') {
162-
output_lines.push(...block.lines)
163-
164-
// Try to sync with original to handle replacements (context consumption)
165-
// We look for a suffix of this block in the original file.
166-
// If found, we advance current_original_idx to the end of that match.
167-
// This allows the block to "consume" original code, avoiding duplication.
168-
169-
const search_window_size = 10
170-
const suffix_lines = block.lines.slice(-search_window_size)
171-
172-
// Heuristic: Find the first occurrence of this suffix in Original after current_original_idx
173-
let found_match = false
174-
let match_end_idx = -1
175-
176-
// Try finding the largest possible suffix that matches
177-
for (let len = suffix_lines.length; len >= 1; len--) {
178-
const sub_suffix = suffix_lines.slice(suffix_lines.length - len)
179-
// Find this sequence of lines in original_lines starting at current_original_idx
180-
const idx = find_line_sequence(
181-
original_lines,
182-
sub_suffix,
183-
current_original_idx
184-
)
185-
if (idx !== -1) {
186-
found_match = true
187-
match_end_idx = idx + len
188-
break
189-
}
190-
}
191-
192-
if (found_match) {
193-
current_original_idx = match_end_idx
194-
}
195-
} else {
196-
// Truncation block
197-
// We need to fill from current_original_idx up to the start of the Next Block.
198-
199-
let fill_end_idx = original_lines.length
200-
201-
if (i + 1 < blocks.length) {
202-
const next_block = blocks[i + 1]
203-
if (next_block.type == 'code') {
204-
// Find where the next block starts in original
205-
const prefix_lines = next_block.lines.slice(0, 10)
206-
let found_next = false
207-
208-
for (let len = prefix_lines.length; len >= 1; len--) {
209-
const sub_prefix = prefix_lines.slice(0, len)
210-
const idx = find_line_sequence(
211-
original_lines,
212-
sub_prefix,
213-
current_original_idx
214-
)
215-
if (idx !== -1) {
216-
fill_end_idx = idx
217-
found_next = true
218-
break
219-
}
220-
}
221-
222-
if (!found_next) {
223-
fill_end_idx = original_lines.length
224-
}
225-
}
226-
}
227-
228-
// Append original content
229-
if (fill_end_idx > current_original_idx) {
230-
output_lines.push(
231-
...original_lines.slice(current_original_idx, fill_end_idx)
232-
)
233-
}
234-
current_original_idx = fill_end_idx
235-
}
236-
}
237-
238-
const result = output_lines.join('\n')
239-
240-
if (original_text.endsWith('\n') && !result.endsWith('\n')) {
241-
return result + '\n'
242-
}
243-
244-
return result
245-
}
246-
247-
const find_line_sequence = (
248-
lines: string[],
249-
sequence: string[],
250-
start_idx: number
251-
): number => {
252-
if (sequence.length == 0) return -1
253-
if (start_idx >= lines.length) return -1
254-
255-
// First pass: exact match (checking for correct indentation)
256-
for (let i = start_idx; i <= lines.length - sequence.length; i++) {
257-
let match = true
258-
for (let j = 0; j < sequence.length; j++) {
259-
if (lines[i + j] != sequence[j]) {
260-
match = false
261-
break
262-
}
263-
}
264-
if (match) return i
265-
}
266-
267-
// Second pass: loose match (trim)
268-
for (let i = start_idx; i <= lines.length - sequence.length; i++) {
269-
let match = true
270-
for (let j = 0; j < sequence.length; j++) {
271-
if (lines[i + j].trim() != sequence[j].trim()) {
272-
match = false
273-
break
274-
}
275-
}
276-
if (match) return i
277-
}
278-
return -1
279-
}

‎apps/editor/src/commands/apply-chat-response-command/utils/conflict-marker-parser/conflict-marker-parser.spec.ts‎

Lines changed: 0 additions & 78 deletions
This file was deleted.

‎apps/editor/src/commands/apply-chat-response-command/utils/conflict-marker-parser/index.ts‎

Lines changed: 0 additions & 1 deletion
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import * as fs from 'fs'
2+
import * as path from 'path'
3+
import { apply_conflict_markers_to_content } from './apply-conflict-markers-to-content'
4+
5+
describe('conflict-marker-parser', () => {
6+
const load_test_case_file = (test_case: string, filename: string): string => {
7+
return fs.readFileSync(
8+
path.join(__dirname, 'test-cases', test_case, filename),
9+
'utf-8'
10+
)
11+
}
12+
13+
const run_test_case = (test_case: string) => {
14+
const original_content = load_test_case_file(test_case, 'original.txt')
15+
const markers_content = load_test_case_file(test_case, 'markers.txt')
16+
const expected_content = load_test_case_file(test_case, 'expected.txt')
17+
18+
const result = apply_conflict_markers_to_content({
19+
original_content,
20+
markers_content
21+
})
22+
23+
expect(result).toBe(expected_content)
24+
}
25+
26+
describe('apply_conflict_markers_to_content', () => {
27+
it('applies a basic conflict marker correctly', () => {
28+
run_test_case('basic-conflict')
29+
})
30+
31+
it('applies multiple conflict markers in a single file', () => {
32+
run_test_case('multiple-conflicts')
33+
})
34+
35+
it('handles deletions via empty updated blocks', () => {
36+
run_test_case('deletion')
37+
})
38+
39+
it('throws an error when context cannot be found', () => {
40+
const original_content = 'Some random text'
41+
const markers_content = '<<<<<<<\nMissing\n=======\nFound\n>>>>>>>'
42+
43+
expect(() =>
44+
apply_conflict_markers_to_content({
45+
original_content,
46+
markers_content
47+
})
48+
).toThrow(/Could not find content to replace/)
49+
})
50+
51+
it('handles whitespace issues in conflict markers', () => {
52+
run_test_case('whitespace-issues')
53+
})
54+
})
55+
})

0 commit comments

Comments
 (0)