Skip to content

Commit fb748ef

Browse files
committed
Refactor response history management by introducing a dedicated ResponseHistoryItem type, centralizing history storage in PanelProvider, and integrating history tracking and preview switching directly into the chat response application flow
1 parent f2e6f15 commit fb748ef

20 files changed

Lines changed: 447 additions & 354 deletions

File tree

‎packages/shared/src/constants/dictionary.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,8 @@ export const dictionary = {
268268
API_KEY_MISSING: 'API key is missing. Please add it in the Settings tab.',
269269
CODE_COMPLETION_ERROR:
270270
'An error occurred during code completion. See console for details.',
271-
REFACTOR_ERROR:
272-
'An error occurred during refactor task. See console for details.',
271+
EDIT_CONTEXT_ERROR:
272+
'An error occurred during context editing. See console for details.',
273273
CONFIGURATION_ALREADY_EXISTS:
274274
'A configuration with these properties already exists',
275275
CONFIGURATION_NOT_FOUND: 'Configuration not found',
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { FileInPreview } from './file-in-preview'
2+
3+
export type ResponseHistoryItem = {
4+
response: string
5+
raw_instructions?: string
6+
created_at: number
7+
lines_added?: number
8+
lines_removed?: number
9+
files?: FileInPreview[]
10+
}

‎packages/ui/src/components/editor/panel/Responses/Responses.tsx‎

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,27 +2,14 @@ import { useEffect, useState } from 'react'
22
import cn from 'classnames'
33
import dayjs from 'dayjs'
44
import relativeTime from 'dayjs/plugin/relativeTime'
5-
import { FileInPreview } from '@shared/types/file-in-preview'
5+
import { ResponseHistoryItem } from '@shared/types/response-history-item'
66
import styles from './Responses.module.scss'
77

88
dayjs.extend(relativeTime)
99

10-
export type ResponseHistoryItem = {
11-
response: string
12-
raw_instructions?: string
13-
created_at: number
14-
lines_added?: number
15-
lines_removed?: number
16-
files?: FileInPreview[]
17-
}
18-
1910
type Props = {
2011
response_history: ResponseHistoryItem[]
21-
on_response_history_item_click: (item: {
22-
response: string
23-
raw_instructions?: string
24-
files?: FileInPreview[]
25-
}) => void
12+
on_response_history_item_click: (item: ResponseHistoryItem) => void
2613
selected_history_item_created_at?: number
2714
on_selected_history_item_change: (created_at: number) => void
2815
}

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

Lines changed: 62 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,10 @@ import {
1818
import { process_chat_response, CommandArgs } from './response-processor'
1919
import { Checkpoint } from '../checkpoints-command/types'
2020
import { CHECKPOINTS_STATE_KEY } from '@/constants/state-keys'
21+
import { ResponseHistoryItem } from '@shared/types/response-history-item'
2122

2223
let in_progress = false
24+
let placeholder_created_at_for_update: number | undefined
2325

2426
export const apply_chat_response_command = (
2527
context: vscode.ExtensionContext,
@@ -52,28 +54,33 @@ export const apply_chat_response_command = (
5254
return
5355
}
5456

55-
if (in_progress) {
56-
panel_provider.send_message({
57-
command: 'NEW_RESPONSE_RECEIVED',
58-
response: chat_response,
59-
raw_instructions: args?.raw_instructions
60-
})
61-
}
62-
6357
if (in_progress && !response_preview_promise_resolve) {
6458
// Running intelligent update before response preview...
6559
return
6660
}
67-
in_progress = true
6861

6962
if (response_preview_promise_resolve) {
7063
// Previewing response...
64+
const history = panel_provider.response_history
65+
66+
const new_item: ResponseHistoryItem = {
67+
response: chat_response,
68+
raw_instructions: args?.raw_instructions,
69+
created_at: Date.now()
70+
}
71+
72+
history.push(new_item)
73+
panel_provider.send_message({
74+
command: 'RESPONSE_HISTORY',
75+
history
76+
})
7177
const choice = await vscode.window.showInformationMessage(
7278
dictionary.warning_message.PREVIEW_ONGOING,
7379
'Switch'
7480
)
7581

7682
if (choice == 'Switch') {
83+
placeholder_created_at_for_update = new_item.created_at
7784
response_preview_promise_resolve({ accepted_files: [] })
7885
if (ongoing_review_cleanup_promise) {
7986
await ongoing_review_cleanup_promise
@@ -84,6 +91,7 @@ export const apply_chat_response_command = (
8491
}
8592
}
8693

94+
in_progress = true
8795
let before_checkpoint: Checkpoint | undefined
8896
try {
8997
before_checkpoint = await create_checkpoint(
@@ -104,6 +112,7 @@ export const apply_chat_response_command = (
104112
)
105113

106114
if (review_data) {
115+
let created_at_for_preview = args?.created_at
107116
if (!args?.files_with_content) {
108117
let total_lines_added = 0
109118
let total_lines_removed = 0
@@ -168,24 +177,58 @@ export const apply_chat_response_command = (
168177
is_checked: true
169178
})
170179
}
180+
const history = panel_provider.response_history
181+
182+
if (placeholder_created_at_for_update) {
183+
const item_to_update = history.find(
184+
(i) => i.created_at === placeholder_created_at_for_update
185+
)
186+
if (item_to_update) {
187+
item_to_update.files = files_for_history
188+
item_to_update.lines_added = total_lines_added
189+
item_to_update.lines_removed = total_lines_removed
190+
}
191+
created_at_for_preview = placeholder_created_at_for_update
192+
placeholder_created_at_for_update = undefined
193+
} else {
194+
const item_to_update =
195+
args?.created_at &&
196+
history.find((i) => i.created_at === args.created_at)
197+
198+
if (item_to_update) {
199+
item_to_update.files = files_for_history
200+
item_to_update.lines_added = total_lines_added
201+
item_to_update.lines_removed = total_lines_removed
202+
} else {
203+
created_at_for_preview = Date.now()
204+
const new_item: ResponseHistoryItem = {
205+
response: review_data.chat_response,
206+
raw_instructions: args?.raw_instructions,
207+
created_at: created_at_for_preview,
208+
lines_added: total_lines_added,
209+
lines_removed: total_lines_removed,
210+
files: files_for_history
211+
}
212+
213+
history.push(new_item)
214+
}
215+
}
171216

172217
panel_provider.send_message({
173-
command: 'NEW_RESPONSE_RECEIVED',
174-
response: review_data.chat_response,
175-
raw_instructions: args?.raw_instructions,
176-
lines_added: total_lines_added,
177-
lines_removed: total_lines_removed,
178-
files: files_for_history
218+
command: 'RESPONSE_HISTORY',
219+
history
179220
})
180221
}
181222

223+
const history_for_checkpoint = [...panel_provider.response_history]
182224
const changes_accepted = await preview_handler({
183225
original_states: review_data.original_states,
184226
chat_response: review_data.chat_response,
185227
panel_provider,
186228
context,
187229
original_editor_state: args?.original_editor_state,
188-
raw_instructions: args?.raw_instructions
230+
raw_instructions: args?.raw_instructions,
231+
created_at: created_at_for_preview
189232
})
190233

191234
if (changes_accepted) {
@@ -200,6 +243,9 @@ export const apply_chat_response_command = (
200243
)
201244
if (checkpoint_to_update) {
202245
checkpoint_to_update.title = 'Before changes approved'
246+
checkpoint_to_update.response_history = history_for_checkpoint
247+
checkpoint_to_update.response_preview_item_created_at =
248+
created_at_for_preview
203249
await context.workspaceState.update(
204250
CHECKPOINTS_STATE_KEY,
205251
checkpoints

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export type CommandArgs = {
3535
position: { line: number; character: number }
3636
}
3737
files_with_content?: FileInPreview[]
38+
created_at?: number
3839
}
3940

4041
export const process_chat_response = async (
@@ -52,7 +53,11 @@ export const process_chat_response = async (
5253
f.file_path === state.file_path &&
5354
f.workspace_name === state.workspace_name
5455
)
55-
return { ...state, ai_content: file_in_review?.content }
56+
return {
57+
...state,
58+
ai_content: file_in_review?.content,
59+
is_checked: file_in_review?.is_checked
60+
}
5661
})
5762
update_undo_button_state({
5863
context,

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

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,21 +18,36 @@ export const preview_handler = async (params: {
1818
position: { line: number; character: number }
1919
}
2020
raw_instructions?: string
21+
created_at?: number
2122
}): Promise<boolean> => {
2223
let resolve_cleanup_promise: () => void
2324
ongoing_review_cleanup_promise = new Promise((resolve) => {
2425
resolve_cleanup_promise = resolve
2526
})
2627

2728
try {
28-
const review_result = await preview({
29+
const preview_result = await preview({
2930
original_states: params.original_states,
3031
panel_provider: params.panel_provider,
3132
raw_instructions: params.raw_instructions,
32-
chat_response: params.chat_response
33+
chat_response: params.chat_response,
34+
context: params.context,
35+
created_at: params.created_at
3336
})
3437

35-
if (review_result === null || review_result.accepted_files.length == 0) {
38+
if (preview_result === null || preview_result.accepted_files.length == 0) {
39+
if (preview_result?.created_at) {
40+
const history = params.panel_provider.response_history
41+
const new_history = history.filter(
42+
(item) => item.created_at !== preview_result.created_at
43+
)
44+
45+
params.panel_provider.response_history = new_history
46+
params.panel_provider.send_message({
47+
command: 'RESPONSE_HISTORY',
48+
history: new_history
49+
})
50+
}
3651
if (params.original_editor_state) {
3752
try {
3853
const uri = vscode.Uri.file(params.original_editor_state.file_path)
@@ -68,21 +83,28 @@ export const preview_handler = async (params: {
6883
return false
6984
}
7085

71-
if (review_result.rejected_states.length > 0) {
86+
if (preview_result.rejected_states.length > 0) {
7287
await undo_files({
73-
original_states: review_result.rejected_states
88+
original_states: preview_result.rejected_states
7489
})
7590
}
7691

7792
const accepted_states = params.original_states.filter((state) =>
78-
review_result.accepted_files.some(
93+
preview_result.accepted_files.some(
7994
(accepted) =>
8095
accepted.file_path == state.file_path &&
8196
accepted.workspace_name == state.workspace_name
8297
)
8398
)
8499

85100
if (accepted_states.length > 0) {
101+
if (preview_result.created_at) {
102+
params.panel_provider.response_history = []
103+
params.panel_provider.send_message({
104+
command: 'RESPONSE_HISTORY',
105+
history: []
106+
})
107+
}
86108
update_undo_button_state({
87109
context: params.context,
88110
panel_provider: params.panel_provider,

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

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,12 @@ export const preview = async (params: {
2121
panel_provider: PanelProvider
2222
raw_instructions?: string
2323
chat_response: string
24+
context: vscode.ExtensionContext
25+
created_at?: number
2426
}): Promise<{
2527
accepted_files: ReviewableFile[]
2628
rejected_states: OriginalFileState[]
29+
created_at?: number
2730
} | null> => {
2831
if (!vscode.workspace.workspaceFolders?.length) {
2932
vscode.window.showErrorMessage(
@@ -125,7 +128,9 @@ export const preview = async (params: {
125128
params.original_states,
126129
params.panel_provider,
127130
workspace_map,
128-
default_workspace
131+
default_workspace,
132+
params.context,
133+
params.created_at
129134
)
130135

131136
create_temp_files_with_original_content(prepared_files)
@@ -160,7 +165,13 @@ export const preview = async (params: {
160165
const { decision } = result
161166

162167
if ('accepted_files' in decision) {
163-
await vscode.workspace.saveAll()
168+
const has_unsaved_editors = vscode.workspace.textDocuments.some(
169+
(doc) => doc.isDirty
170+
)
171+
if (has_unsaved_editors) {
172+
await vscode.workspace.saveAll()
173+
await new Promise((resolve) => setTimeout(resolve, 500))
174+
}
164175

165176
const accepted_files_info = decision.accepted_files
166177
const accepted_file_identifiers = new Set(
@@ -194,7 +205,11 @@ export const preview = async (params: {
194205
})
195206
.filter((state): state is OriginalFileState => state !== undefined)
196207

197-
return { accepted_files, rejected_states }
208+
return {
209+
accepted_files,
210+
rejected_states,
211+
created_at: decision.created_at
212+
}
198213
}
199214

200215
if ('jump_to' in decision) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { FileInPreview } from '@shared/types/file-in-preview'
22

33
export type CodeReviewDecision =
44
| { jump_to: { file_path: string; workspace_name?: string } }
5-
| { accepted_files: FileInPreview[] }
5+
| { accepted_files: FileInPreview[]; created_at?: number }
66

77
export type CodeReviewResult = {
88
decision: CodeReviewDecision

0 commit comments

Comments
 (0)