Skip to content

Commit cd9ce02

Browse files
committed
Merge branch 'dev'
2 parents a464549 + a21cf57 commit cd9ce02

5 files changed

Lines changed: 316 additions & 45 deletions

File tree

‎packages/vscode/src/commands/apply-chat-response-command/utils/review.ts‎

Lines changed: 265 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ const show_diff_with_actions = async (
287287

288288
export const review = async (params: {
289289
original_states: OriginalFileState[]
290-
view_provider?: ViewProvider
290+
view_provider: ViewProvider
291291
raw_instructions?: string
292292
}): Promise<{
293293
accepted_files: ReviewableFile[]
@@ -314,11 +314,9 @@ export const review = async (params: {
314314

315315
const text_document_change_listener =
316316
vscode.workspace.onDidChangeTextDocument((event) => {
317-
if (!params.view_provider) return
318-
319317
const changed_doc_path = event.document.uri.fsPath
320318
const changed_file_in_review = prepared_files.find(
321-
(pf) => pf.sanitized_path === changed_doc_path
319+
(pf) => pf.sanitized_path == changed_doc_path
322320
)
323321

324322
if (changed_file_in_review) {
@@ -339,9 +337,244 @@ export const review = async (params: {
339337
command: 'UPDATE_FILE_IN_REVIEW',
340338
file: changed_file_in_review.reviewable_file
341339
})
340+
} else {
341+
// A file not in the review has been changed. Add it to the review.
342+
const doc = event.document
343+
if (doc.uri.scheme != 'file') return
344+
const workspace_folder = vscode.workspace.getWorkspaceFolder(doc.uri)
345+
if (!workspace_folder) return
346+
347+
// Check if it's already added to avoid races
348+
if (prepared_files.some((pf) => pf.sanitized_path == doc.uri.fsPath)) {
349+
return
350+
}
351+
352+
const new_content = doc.getText()
353+
const relative_path = vscode.workspace
354+
.asRelativePath(doc.uri, false)
355+
.replace(/\\/g, '/')
356+
357+
let original_content = ''
358+
let is_new = false
359+
if (doc.isUntitled) {
360+
is_new = true
361+
} else {
362+
try {
363+
original_content = fs.readFileSync(doc.uri.fsPath, 'utf8')
364+
} catch (e) {
365+
is_new = true // File on disk does not exist, but buffer does.
366+
}
367+
}
368+
369+
const new_original_state: OriginalFileState = {
370+
file_path: relative_path,
371+
content: original_content,
372+
is_new: is_new,
373+
workspace_name: workspace_folder.name
374+
}
375+
376+
const diff_stats = get_diff_stats({
377+
original_content: original_content,
378+
new_content: new_content
379+
})
380+
381+
const sanitized_file_path = doc.uri.fsPath
382+
const hash = crypto
383+
.createHash('md5')
384+
.update(sanitized_file_path)
385+
.digest('hex')
386+
const temp_filename = `cwc-review-${hash}.tmp`
387+
const temp_file_path = path.join(os.tmpdir(), temp_filename)
388+
const is_deleted =
389+
!is_new && new_content == '' && original_content != ''
390+
391+
const reviewable_file: ReviewableFile = {
392+
file_path: relative_path,
393+
content: new_content,
394+
workspace_name: workspace_folder.name,
395+
is_new: is_new,
396+
is_deleted: is_deleted,
397+
lines_added: diff_stats.lines_added,
398+
lines_removed: diff_stats.lines_removed
399+
}
400+
401+
const new_prepared_file: PreparedFile = {
402+
reviewable_file,
403+
sanitized_path: sanitized_file_path,
404+
original_content: original_content,
405+
temp_file_path,
406+
file_exists: !is_new
407+
}
408+
409+
params.original_states.push(new_original_state)
410+
prepared_files.push(new_prepared_file)
411+
create_temp_files_with_original_content([new_prepared_file])
412+
params.view_provider.send_message({
413+
command: 'UPDATE_FILE_IN_REVIEW',
414+
file: new_prepared_file.reviewable_file
415+
})
342416
}
343417
})
344418

419+
const file_delete_listener = vscode.workspace.onDidDeleteFiles((event) => {
420+
for (const uri of event.files) {
421+
if (uri.scheme != 'file') continue
422+
423+
const deleted_file_path = uri.fsPath
424+
const deleted_file_in_review = prepared_files.find(
425+
(pf) => pf.sanitized_path == deleted_file_path
426+
)
427+
428+
if (!deleted_file_in_review) {
429+
const workspace_folder = vscode.workspace.getWorkspaceFolder(uri)
430+
if (!workspace_folder) continue
431+
432+
if (prepared_files.some((pf) => pf.sanitized_path == uri.fsPath)) {
433+
continue
434+
}
435+
436+
const new_content = ''
437+
const relative_path = vscode.workspace
438+
.asRelativePath(uri, false)
439+
.replace(/\\/g, '/')
440+
441+
const original_content_for_undo = '' // Limitation: cannot get content of deleted file
442+
443+
const new_original_state: OriginalFileState = {
444+
file_path: relative_path,
445+
content: original_content_for_undo,
446+
is_new: false,
447+
workspace_name: workspace_folder.name
448+
}
449+
450+
const diff_stats = get_diff_stats({
451+
original_content: original_content_for_undo,
452+
new_content: new_content
453+
})
454+
455+
const sanitized_file_path = uri.fsPath
456+
const hash = crypto
457+
.createHash('md5')
458+
.update(sanitized_file_path)
459+
.digest('hex')
460+
const temp_filename = `cwc-review-${hash}.tmp`
461+
const temp_file_path = path.join(os.tmpdir(), temp_filename)
462+
463+
const reviewable_file: ReviewableFile = {
464+
file_path: relative_path,
465+
content: new_content,
466+
workspace_name: workspace_folder.name,
467+
is_new: false,
468+
is_deleted: true,
469+
lines_added: diff_stats.lines_added,
470+
lines_removed: diff_stats.lines_removed
471+
}
472+
473+
const new_prepared_file: PreparedFile = {
474+
reviewable_file,
475+
sanitized_path: sanitized_file_path,
476+
original_content: original_content_for_undo,
477+
temp_file_path,
478+
file_exists: false
479+
}
480+
481+
params.original_states.push(new_original_state)
482+
prepared_files.push(new_prepared_file)
483+
create_temp_files_with_original_content([new_prepared_file])
484+
params.view_provider.send_message({
485+
command: 'UPDATE_FILE_IN_REVIEW',
486+
file: new_prepared_file.reviewable_file
487+
})
488+
} else {
489+
deleted_file_in_review.reviewable_file.is_deleted = true
490+
deleted_file_in_review.reviewable_file.content = ''
491+
params.view_provider.send_message({
492+
command: 'UPDATE_FILE_IN_REVIEW',
493+
file: deleted_file_in_review.reviewable_file
494+
})
495+
}
496+
}
497+
})
498+
499+
const file_created_listener = vscode.workspace.onDidCreateFiles((event) => {
500+
for (const uri of event.files) {
501+
if (uri.scheme !== 'file') continue
502+
503+
const workspace_folder = vscode.workspace.getWorkspaceFolder(uri)
504+
if (!workspace_folder) continue
505+
506+
// Avoid duplicates if already tracked
507+
if (prepared_files.some((pf) => pf.sanitized_path === uri.fsPath)) {
508+
continue
509+
}
510+
511+
// Read the content of the newly created file (may be empty)
512+
let new_content = ''
513+
try {
514+
new_content = fs.readFileSync(uri.fsPath, 'utf8')
515+
} catch (e) {
516+
new_content = ''
517+
}
518+
519+
const relative_path = vscode.workspace
520+
.asRelativePath(uri, false)
521+
.replace(/\\/g, '/')
522+
523+
const original_content = '' // New file had no previous content
524+
const is_new = true
525+
526+
const new_original_state: OriginalFileState = {
527+
file_path: relative_path,
528+
content: original_content,
529+
is_new: is_new,
530+
workspace_name: workspace_folder.name
531+
}
532+
533+
const diff_stats = get_diff_stats({
534+
original_content: original_content,
535+
new_content: new_content
536+
})
537+
538+
const sanitized_file_path = uri.fsPath
539+
const hash = crypto
540+
.createHash('md5')
541+
.update(sanitized_file_path)
542+
.digest('hex')
543+
const temp_filename = `cwc-review-${hash}.tmp`
544+
const temp_file_path = path.join(os.tmpdir(), temp_filename)
545+
546+
const reviewable_file: ReviewableFile = {
547+
file_path: relative_path,
548+
content: new_content,
549+
workspace_name: workspace_folder.name,
550+
is_new: true,
551+
is_deleted: false,
552+
lines_added: diff_stats.lines_added,
553+
lines_removed: diff_stats.lines_removed
554+
}
555+
556+
const new_prepared_file: PreparedFile = {
557+
reviewable_file,
558+
sanitized_path: sanitized_file_path,
559+
original_content: original_content,
560+
temp_file_path,
561+
file_exists: false
562+
}
563+
564+
params.original_states.push(new_original_state)
565+
prepared_files.push(new_prepared_file)
566+
567+
// Create temp file with the original (empty) content for diff view
568+
create_temp_files_with_original_content([new_prepared_file])
569+
570+
// Notify the panel to include this file in the review UI
571+
params.view_provider.send_message({
572+
command: 'UPDATE_FILE_IN_REVIEW',
573+
file: new_prepared_file.reviewable_file
574+
})
575+
}
576+
})
577+
345578
try {
346579
prepared_files = await prepare_files_from_original_states({
347580
original_states: params.original_states,
@@ -474,15 +707,32 @@ export const review = async (params: {
474707
)
475708
)
476709

477-
review_items.forEach((item) => {
478-
const file_identifier = `${
479-
item.file.reviewable_file.workspace_name || ''
480-
}:${item.file.reviewable_file.file_path}`
481-
item.status = accepted_file_identifiers.has(file_identifier)
482-
? 'accepted'
483-
: 'rejected'
484-
})
485-
break
710+
const accepted_files = prepared_files
711+
.filter((pf) => {
712+
const identifier = `${pf.reviewable_file.workspace_name || ''}:${
713+
pf.reviewable_file.file_path
714+
}`
715+
return accepted_file_identifiers.has(identifier)
716+
})
717+
.map((pf) => pf.reviewable_file)
718+
719+
const rejected_states = prepared_files
720+
.filter((pf) => {
721+
const identifier = `${pf.reviewable_file.workspace_name || ''}:${
722+
pf.reviewable_file.file_path
723+
}`
724+
return !accepted_file_identifiers.has(identifier)
725+
})
726+
.map((item) => {
727+
return params.original_states.find(
728+
(state) =>
729+
state.file_path == item.reviewable_file.file_path &&
730+
state.workspace_name == item.reviewable_file.workspace_name
731+
)
732+
})
733+
.filter((state): state is OriginalFileState => state !== undefined)
734+
735+
return { accepted_files, rejected_states }
486736
}
487737

488738
if ('jump_to' in decision) {
@@ -526,6 +776,8 @@ export const review = async (params: {
526776
return { accepted_files, rejected_states }
527777
} finally {
528778
text_document_change_listener.dispose()
779+
file_delete_listener.dispose()
780+
file_created_listener.dispose()
529781
await close_review_diff_editors(prepared_files)
530782
cleanup_temp_files(prepared_files)
531783
toggle_file_review_state = undefined

‎packages/vscode/src/commands/delete-command.ts‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,15 @@ export function delete_command() {
4242
)
4343
}
4444

45-
await vscode.workspace.fs.delete(uri, {
46-
recursive: true
45+
const edit = new vscode.WorkspaceEdit()
46+
edit.deleteFile(uri, {
47+
recursive: true,
48+
ignoreIfNotExists: true
4749
})
50+
const applied = await vscode.workspace.applyEdit(edit)
51+
if (!applied) {
52+
throw new Error('Failed to apply delete edit')
53+
}
4854
} catch (error: any) {
4955
vscode.window.showErrorMessage(
5056
dictionary.error_message.FAILED_TO_DELETE(error.message)

0 commit comments

Comments
 (0)