Skip to content

Commit 22a6566

Browse files
committed
Refactor the "Refactor Current File" command and the Intelligent Update API calls to display detailed progress notifications (Waiting, Thinking, Receiving) and persist the last used refactoring instruction source and content
1 parent b702ab9 commit 22a6566

7 files changed

Lines changed: 381 additions & 182 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ Generate meaningful summaries of changes adhering to your style.
9898
### Handling chat responses
9999

100100
- `Code Web Chat: Apply Chat Response` - Integrate with the codebase a copied to clipboard overall chat response or a single code block.
101-
- `Code Web Chat: Refactor Current File` - Use Intelligent Update API tool for the active editor, without context.
101+
- `Code Web Chat: Refactor Current File` - Use Intelligent Update API tool for the active editor, without context. Accepts clipboard text or instructions.
102102
- `Code Web Chat: Undo Last Changes` - Revert applied AI-suggested edits.
103103

104104
### Code completions

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
export const dictionary = {
22
api_call: {
33
WAITING_FOR_API_RESPONSE: 'Waiting for API response',
4-
THINKING: 'Thinking'
4+
THINKING: 'Thinking',
5+
RECEIVING_RESPONSE: 'Receiving response'
56
},
67

78
'ChatInput.tsx': {

‎packages/vscode/src/commands/apply-chat-response-command/handlers/intelligent-update-handler.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ export const handle_intelligent_update = async (params: {
286286
const updated_content_result = await process_file({
287287
endpoint_url: params.endpoint_url,
288288
api_key: params.api_key,
289+
provider: { name: params.config.provider_name },
289290
model: params.config.model,
290291
temperature: params.config.temperature,
291292
reasoning_effort: params.config.reasoning_effort,

‎packages/vscode/src/commands/refactor-current-file-command.ts‎

Lines changed: 204 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ import {
77
process_file
88
} from '../utils/intelligent-update-utils'
99
import { dictionary } from '@shared/constants/dictionary'
10+
import {
11+
LAST_REFACTOR_INSTRUCTION_SOURCE_STATE_KEY,
12+
LAST_REFACTOR_INSTRUCTION_STATE_KEY
13+
} from '../constants/state-keys'
14+
import { Logger } from '@shared/utils/logger'
1015

1116
export const refactor_current_file_command = (
1217
context: vscode.ExtensionContext
@@ -24,25 +29,82 @@ export const refactor_current_file_command = (
2429

2530
let instruction: string | undefined
2631
const clipboard_content = await vscode.env.clipboard.readText()
32+
33+
const last_instruction_source = context.workspaceState.get<string>(
34+
LAST_REFACTOR_INSTRUCTION_SOURCE_STATE_KEY
35+
)
36+
const last_instruction = context.workspaceState.get<string>(
37+
LAST_REFACTOR_INSTRUCTION_STATE_KEY
38+
)
39+
2740
if (!clipboard_content) {
2841
instruction = await vscode.window.showInputBox({
29-
prompt: 'Enter your refactoring instructions'
42+
prompt: 'Enter your refactoring instructions',
43+
value: last_instruction
3044
})
45+
if (instruction) {
46+
await context.workspaceState.update(
47+
LAST_REFACTOR_INSTRUCTION_STATE_KEY,
48+
instruction
49+
)
50+
await context.workspaceState.update(
51+
LAST_REFACTOR_INSTRUCTION_SOURCE_STATE_KEY,
52+
'Instructions'
53+
)
54+
}
3155
} else {
32-
const choice = await vscode.window.showQuickPick(
33-
['Clipboard', 'Instructions'],
34-
{
35-
placeHolder:
36-
'Use clipboard content or enter new refactoring instructions?'
56+
const choice = await new Promise<string | undefined>((resolve) => {
57+
const quick_pick = vscode.window.createQuickPick()
58+
const items = [{ label: 'Clipboard' }, { label: 'Instructions' }]
59+
quick_pick.items = items
60+
quick_pick.placeholder =
61+
'Use clipboard content or enter new refactoring instructions?'
62+
63+
if (last_instruction_source) {
64+
const active_item = items.find(
65+
(item) => item.label === last_instruction_source
66+
)
67+
if (active_item) {
68+
quick_pick.activeItems = [active_item]
69+
}
3770
}
71+
72+
quick_pick.onDidAccept(() => {
73+
const selected = quick_pick.selectedItems[0]
74+
resolve(selected?.label)
75+
quick_pick.hide()
76+
})
77+
78+
quick_pick.onDidHide(() => {
79+
quick_pick.dispose()
80+
resolve(undefined)
81+
})
82+
83+
quick_pick.show()
84+
})
85+
86+
if (choice === undefined) {
87+
return // User cancelled
88+
}
89+
90+
await context.workspaceState.update(
91+
LAST_REFACTOR_INSTRUCTION_SOURCE_STATE_KEY,
92+
choice
3893
)
3994

4095
if (choice == 'Clipboard') {
4196
instruction = clipboard_content
4297
} else if (choice == 'Instructions') {
4398
instruction = await vscode.window.showInputBox({
44-
prompt: 'Enter your refactoring instructions'
99+
prompt: 'Enter your refactoring instructions',
100+
value: last_instruction
45101
})
102+
if (instruction) {
103+
await context.workspaceState.update(
104+
LAST_REFACTOR_INSTRUCTION_STATE_KEY,
105+
instruction
106+
)
107+
}
46108
}
47109
}
48110

@@ -74,89 +136,161 @@ export const refactor_current_file_command = (
74136
const document = editor.document
75137
const original_content = document.getText()
76138
const file_path = document.uri.fsPath
139+
const cancel_token_source = axios.CancelToken.source()
140+
141+
let thinking_reported = false
142+
let resolve_thinking: () => void
143+
const thinking_promise = new Promise<void>((resolve) => {
144+
resolve_thinking = resolve
145+
})
146+
147+
const on_thinking_chunk = () => {
148+
if (!thinking_reported) {
149+
thinking_reported = true
150+
resolve_thinking()
151+
}
152+
}
153+
154+
let receiving_reported = false
155+
let resolve_receiving: () => void
156+
const receiving_promise = new Promise<void>((resolve) => {
157+
resolve_receiving = resolve
158+
})
159+
160+
const on_chunk = () => {
161+
if (!receiving_reported) {
162+
receiving_reported = true
163+
resolve_receiving()
164+
}
165+
}
166+
167+
const content_promise = process_file({
168+
endpoint_url,
169+
api_key: provider.api_key,
170+
provider,
171+
model: intelligent_update_config.model,
172+
temperature: intelligent_update_config.temperature,
173+
reasoning_effort: intelligent_update_config.reasoning_effort,
174+
file_path: file_path,
175+
file_content: original_content,
176+
instruction,
177+
cancel_token: cancel_token_source.token,
178+
on_thinking_chunk,
179+
on_chunk
180+
})
77181

78-
let result: any
79182
try {
80-
result = await vscode.window.withProgress(
183+
await vscode.window.withProgress(
81184
{
82185
location: vscode.ProgressLocation.Notification,
83-
title: `Refactoring current file`,
186+
title: dictionary.api_call.WAITING_FOR_API_RESPONSE,
84187
cancellable: true
85188
},
86189
async (progress, token) => {
87-
const cancel_token_source = axios.CancelToken.source()
88190
token.onCancellationRequested(() => {
89-
cancel_token_source.cancel('Cancelled by user.')
191+
cancel_token_source.cancel('User cancelled the operation')
90192
})
91193

92-
let previous_progress = 0
93-
const estimated_total_tokens = Math.ceil(
94-
original_content.length / 4
95-
)
194+
let wait_time = 0
195+
const wait_timer = setInterval(() => {
196+
progress.report({
197+
message: `${(wait_time / 10).toFixed(1)}s`
198+
})
199+
wait_time++
200+
}, 100)
201+
202+
await Promise.race([
203+
content_promise,
204+
thinking_promise,
205+
receiving_promise
206+
])
207+
clearInterval(wait_timer)
208+
}
209+
)
96210

97-
const content = await process_file({
98-
endpoint_url,
99-
api_key: provider.api_key,
100-
model: intelligent_update_config.model,
101-
temperature: intelligent_update_config.temperature,
102-
reasoning_effort: intelligent_update_config.reasoning_effort,
103-
file_path: file_path,
104-
file_content: original_content,
105-
instruction,
106-
cancel_token: cancel_token_source.token,
107-
on_chunk: (tokens_per_second, total_tokens) => {
108-
if (estimated_total_tokens > 0) {
109-
const current_progress = Math.min(
110-
Math.round((total_tokens / estimated_total_tokens) * 100),
111-
100
112-
)
113-
const increment = current_progress - previous_progress
114-
if (increment > 0) {
115-
progress.report({
116-
increment,
117-
message: `~${tokens_per_second} tokens/s`
118-
})
119-
previous_progress = current_progress
120-
}
121-
}
211+
if (thinking_reported && !receiving_reported) {
212+
await vscode.window.withProgress(
213+
{
214+
location: vscode.ProgressLocation.Notification,
215+
title: dictionary.api_call.THINKING,
216+
cancellable: true
217+
},
218+
async (progress, token) => {
219+
token.onCancellationRequested(() => {
220+
cancel_token_source.cancel('User cancelled the operation')
221+
})
222+
223+
let thinking_time = 0
224+
const thinking_timer = setInterval(() => {
225+
progress.report({
226+
message: `${(thinking_time / 10).toFixed(1)}s`
227+
})
228+
thinking_time++
229+
}, 100)
230+
231+
try {
232+
await Promise.race([content_promise, receiving_promise])
233+
} finally {
234+
clearInterval(thinking_timer)
122235
}
123-
})
236+
}
237+
)
238+
}
239+
240+
if (receiving_reported) {
241+
await vscode.window.withProgress(
242+
{
243+
location: vscode.ProgressLocation.Notification,
244+
title: dictionary.api_call.RECEIVING_RESPONSE,
245+
cancellable: true
246+
},
247+
async (progress, token) => {
248+
token.onCancellationRequested(() => {
249+
cancel_token_source.cancel('User cancelled the operation')
250+
})
124251

125-
return {
126-
content,
127-
cancelled: token.isCancellationRequested
252+
let receiving_time = 0
253+
const receiving_timer = setInterval(() => {
254+
progress.report({
255+
message: `${(receiving_time / 10).toFixed(1)}s`
256+
})
257+
receiving_time++
258+
}, 100)
259+
260+
try {
261+
await content_promise
262+
} finally {
263+
clearInterval(receiving_timer)
264+
}
128265
}
129-
}
130-
)
131-
} catch (error) {
132-
if (axios.isCancel(error)) {
133-
return
266+
)
134267
}
135-
throw error
136-
}
137268

138-
if (result.cancelled) {
139-
return
140-
}
269+
const updated_content = await content_promise
141270

142-
const updated_content = result.content
271+
if (updated_content) {
272+
const relative_path = vscode.workspace.asRelativePath(document.uri)
273+
const response_for_apply = `\`\`\`\n// ${relative_path}\n${updated_content}\n\`\`\``
143274

144-
if (updated_content) {
145-
const relative_path = vscode.workspace.asRelativePath(document.uri)
146-
const response_for_apply = `\`\`\`\n// ${relative_path}\n${updated_content}\n\`\`\``
275+
await vscode.commands.executeCommand(
276+
'codeWebChat.applyChatResponse',
277+
{
278+
response: response_for_apply,
279+
suppress_fast_replace_inaccuracies_dialog: true
280+
}
281+
)
147282

148-
await vscode.commands.executeCommand('codeWebChat.applyChatResponse', {
149-
response: response_for_apply,
150-
suppress_fast_replace_inaccuracies_dialog: true
283+
await vscode.window.showInformationMessage(
284+
dictionary.information_message
285+
.CLIPBOARD_CONTENT_APPLIED_SUCCESSFULLY
286+
)
287+
}
288+
} catch (err: any) {
289+
Logger.error({
290+
function_name: 'refactorCurrentFile',
291+
message: 'Refactor error',
292+
data: err
151293
})
152-
153-
await vscode.window.showInformationMessage(
154-
dictionary.information_message.CLIPBOARD_CONTENT_APPLIED_SUCCESSFULLY
155-
)
156-
} else {
157-
vscode.window.showErrorMessage(
158-
dictionary.error_message.FAILED_TO_APPLY_CLIPBOARD_CONTENT
159-
)
160294
}
161295
}
162296
)

‎packages/vscode/src/constants/state-keys.ts‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export const LAST_CHOSEN_COMMAND_BY_VIEW_TYPE_STATE_KEY =
3939
'lastChosenCommandByViewType'
4040
export const LAST_CONTEXT_MERGE_REPLACE_OPTION_STATE_KEY =
4141
'last-context-merge-replace-option'
42+
export const LAST_REFACTOR_INSTRUCTION_SOURCE_STATE_KEY =
43+
'last-refactor-instruction-source'
44+
export const LAST_REFACTOR_INSTRUCTION_STATE_KEY = 'last-refactor-instruction'
4245

4346
export const CHAT_EDIT_FORMAT_STATE_KEY = 'chat-edit-format'
4447
export const API_EDIT_FORMAT_STATE_KEY = 'api-edit-format'

0 commit comments

Comments
 (0)