Skip to content

Commit fb0eab0

Browse files
committed
Refactor checkpoint management code into dedicated action files and remove unnecessary UI focus logic
1 parent 07edf21 commit fb0eab0

21 files changed

Lines changed: 732 additions & 685 deletions

‎README.md‎

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

125125
- `Code Web Chat: Apply Chat Response` - Integrate with the codebase a copied to clipboard overall chat response or a single code block.
126-
- `Code Web Chat: Apply Code Block to Active Editor` - Use Intelligent Update API tool on the active editor.
126+
- `Code Web Chat: Apply Code Block to Active Editor` - Use Intelligent Update API tool on the active editor with the copied to clipboard.
127127
- `Code Web Chat: Undo Last Changes` - Revert applied AI-suggested edits.
128128

129129
### Code completions

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

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,6 @@ export const Changes: FC<Props> = ({
3737
raw_instructions
3838
}) => {
3939
const [last_clicked_file_index, set_last_clicked_file_index] = useState(0)
40-
const [is_keep_button_focused, set_is_keep_button_focused] = useState(false)
41-
42-
useEffect(() => {
43-
const timer = setTimeout(() => {
44-
set_is_keep_button_focused(true)
45-
}, 1000)
46-
47-
return () => clearTimeout(timer)
48-
}, [])
4940

5041
const handle_keep = () => {
5142
const accepted_files = files.filter((f) => f.is_checked)
@@ -217,7 +208,6 @@ export const Changes: FC<Props> = ({
217208
<Button
218209
on_click={handle_keep}
219210
disabled={files.filter((f) => f.is_checked).length == 0}
220-
is_focused={is_keep_button_focused}
221211
>
222212
<span className="codicon codicon-check" />
223213
{files.length > 1 ? 'Approve Selected' : 'Approve'}

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,6 @@ import {
1717
ModelProvidersManager,
1818
get_tool_config_id
1919
} from '@/services/model-providers-manager'
20-
import {
21-
create_temporary_checkpoint,
22-
delete_checkpoint,
23-
promote_temporary_checkpoint
24-
} from '@/commands/checkpoints-command'
2520
import { apply_git_patch } from './handlers/diff-handler'
2621
import { PROVIDERS } from '@shared/constants/providers'
2722
import { LAST_SELECTED_INTELLIGENT_UPDATE_CONFIG_ID_STATE_KEY } from '@/constants/state-keys'
@@ -30,6 +25,11 @@ import { ViewProvider } from '@/views/panel/backend/panel-provider'
3025
import { review, code_review_promise_resolve } from './utils/review'
3126
import { dictionary } from '@shared/constants/dictionary'
3227
import { WorkspaceProvider } from '@/context/providers/workspace-provider'
28+
import {
29+
create_temporary_checkpoint,
30+
delete_checkpoint,
31+
promote_temporary_checkpoint
32+
} from '../checkpoints-command/actions'
3333

3434
let ongoing_review_cleanup_promise: Promise<void> | null = null
3535

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import * as vscode from 'vscode'
2+
import * as path from 'path'
3+
import { WorkspaceProvider } from '../../../context/providers/workspace-provider'
4+
import { copy_optimised_recursively } from '../utils'
5+
6+
export const copy_workspace_to_dir = async (
7+
dest_dir_uri: vscode.Uri,
8+
workspace_provider: WorkspaceProvider
9+
) => {
10+
const workspace_folders = vscode.workspace.workspaceFolders!
11+
const dest_dir_path = dest_dir_uri.fsPath
12+
13+
for (const folder of workspace_folders) {
14+
const dest_folder_path =
15+
workspace_folders.length > 1
16+
? path.join(dest_dir_path, folder.name)
17+
: dest_dir_path
18+
const dest_folder_uri = vscode.Uri.file(dest_folder_path)
19+
if (workspace_folders.length > 1) {
20+
await vscode.workspace.fs.createDirectory(dest_folder_uri)
21+
}
22+
23+
const entries = await vscode.workspace.fs.readDirectory(folder.uri)
24+
for (const [name] of entries) {
25+
await copy_optimised_recursively(
26+
vscode.Uri.joinPath(folder.uri, name),
27+
vscode.Uri.joinPath(dest_folder_uri, name),
28+
folder.uri.fsPath,
29+
workspace_provider
30+
)
31+
}
32+
}
33+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import * as vscode from 'vscode'
2+
import { CHECKPOINTS_STATE_KEY } from '../../../constants/state-keys'
3+
import { WorkspaceProvider } from '../../../context/providers/workspace-provider'
4+
import type { Checkpoint } from '../types'
5+
import { copy_workspace_to_dir } from './copy-workspace-to-dir'
6+
import { get_checkpoints } from './get-checkpoints'
7+
import { get_checkpoint_path } from '../utils'
8+
9+
export const create_checkpoint = async (
10+
workspace_provider: WorkspaceProvider,
11+
context: vscode.ExtensionContext
12+
): Promise<boolean> => {
13+
try {
14+
const timestamp = Date.now()
15+
await vscode.window.withProgress(
16+
{
17+
location: vscode.ProgressLocation.Notification,
18+
title: 'Creating checkpoint...',
19+
cancellable: false
20+
},
21+
async () => {
22+
await vscode.workspace.saveAll()
23+
const checkpoint_dir_path = get_checkpoint_path(timestamp)
24+
const checkpoint_dir_uri = vscode.Uri.file(checkpoint_dir_path)
25+
await vscode.workspace.fs.createDirectory(checkpoint_dir_uri)
26+
27+
await copy_workspace_to_dir(checkpoint_dir_uri, workspace_provider)
28+
29+
const new_checkpoint: Checkpoint = {
30+
title: 'Created by user',
31+
timestamp
32+
}
33+
34+
const checkpoints = await get_checkpoints(context)
35+
checkpoints.push(new_checkpoint)
36+
await context.workspaceState.update(CHECKPOINTS_STATE_KEY, checkpoints)
37+
}
38+
)
39+
return true
40+
} catch (err: any) {
41+
vscode.window.showErrorMessage(
42+
`Failed to create checkpoint: ${err.message}`
43+
)
44+
return false
45+
}
46+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as vscode from 'vscode'
2+
import { WorkspaceProvider } from '../../../context/providers/workspace-provider'
3+
import type { Checkpoint } from '../types'
4+
import { copy_workspace_to_dir } from './copy-workspace-to-dir'
5+
import { get_checkpoint_path } from '../utils'
6+
7+
export const create_temporary_checkpoint = async (
8+
workspace_provider: WorkspaceProvider
9+
): Promise<Checkpoint> => {
10+
await vscode.workspace.saveAll()
11+
const timestamp = Date.now()
12+
const checkpoint_dir_path = get_checkpoint_path(timestamp)
13+
const checkpoint_dir_uri = vscode.Uri.file(checkpoint_dir_path)
14+
await vscode.workspace.fs.createDirectory(checkpoint_dir_uri)
15+
16+
await copy_workspace_to_dir(checkpoint_dir_uri, workspace_provider)
17+
18+
const new_checkpoint: Checkpoint = {
19+
timestamp,
20+
title: '',
21+
is_temporary: true
22+
}
23+
return new_checkpoint
24+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import * as vscode from 'vscode'
2+
import dayjs from 'dayjs'
3+
import { CHECKPOINTS_STATE_KEY } from '../../../constants/state-keys'
4+
import type { Checkpoint } from '../types'
5+
import { get_checkpoint_path } from '../utils'
6+
7+
export const delete_checkpoint = async (params: {
8+
context: vscode.ExtensionContext
9+
checkpoint_to_delete: Checkpoint
10+
options?: { skip_undo_prompt?: boolean }
11+
}) => {
12+
// First, remove checkpoint from state. This makes the deletion immediate from user's perspective.
13+
const checkpoints =
14+
params.context.workspaceState.get<Checkpoint[]>(
15+
CHECKPOINTS_STATE_KEY,
16+
[]
17+
) ?? []
18+
const updated_checkpoints = checkpoints.filter(
19+
(c) => c.timestamp != params.checkpoint_to_delete.timestamp
20+
)
21+
await params.context.workspaceState.update(
22+
CHECKPOINTS_STATE_KEY,
23+
updated_checkpoints
24+
)
25+
26+
const actually_delete_files = async () => {
27+
try {
28+
const checkpoint_path = get_checkpoint_path(
29+
params.checkpoint_to_delete.timestamp
30+
)
31+
await vscode.workspace.fs.delete(vscode.Uri.file(checkpoint_path), {
32+
recursive: true
33+
})
34+
} catch (error) {
35+
console.warn(`Could not delete checkpoint file: ${error}`)
36+
}
37+
}
38+
39+
if (!params.options?.skip_undo_prompt) {
40+
vscode.window
41+
.showInformationMessage(
42+
`Checkpoint from ${dayjs(
43+
params.checkpoint_to_delete.timestamp
44+
).fromNow()} deleted successfully.`,
45+
'Revert'
46+
)
47+
.then(async (action) => {
48+
if (action == 'Revert') {
49+
// User chose to revert. Add the checkpoint back to the state.
50+
const current_checkpoints =
51+
params.context.workspaceState.get<Checkpoint[]>(
52+
CHECKPOINTS_STATE_KEY,
53+
[]
54+
) ?? []
55+
current_checkpoints.push(params.checkpoint_to_delete)
56+
await params.context.workspaceState.update(
57+
CHECKPOINTS_STATE_KEY,
58+
current_checkpoints
59+
)
60+
vscode.window.showInformationMessage('Checkpoint deletion reverted.')
61+
} else {
62+
// User dismissed the notification or it timed out.
63+
// Proceed with deleting the checkpoint files.
64+
await actually_delete_files()
65+
}
66+
})
67+
} else {
68+
// This is a silent deletion (e.g. temporary checkpoint cleanup), no need
69+
// to ask for revert.
70+
await actually_delete_files()
71+
}
72+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import * as vscode from 'vscode'
2+
import { CHECKPOINTS_STATE_KEY } from '../../../constants/state-keys'
3+
import type { Checkpoint } from '../types'
4+
5+
export const edit_checkpoint = async (params: {
6+
context: vscode.ExtensionContext
7+
checkpoint_to_edit: Checkpoint
8+
}) => {
9+
const new_description = await vscode.window.showInputBox({
10+
prompt: 'Enter a description for the checkpoint',
11+
value: params.checkpoint_to_edit.description || '',
12+
placeHolder: 'e.g. Before refactoring the main component'
13+
})
14+
15+
if (new_description === undefined) return
16+
17+
const checkpoints =
18+
params.context.workspaceState.get<Checkpoint[]>(
19+
CHECKPOINTS_STATE_KEY,
20+
[]
21+
) ?? []
22+
const checkpoint_to_update = checkpoints.find(
23+
(c) => c.timestamp == params.checkpoint_to_edit.timestamp
24+
)
25+
if (checkpoint_to_update) {
26+
checkpoint_to_update.description = new_description
27+
await params.context.workspaceState.update(
28+
CHECKPOINTS_STATE_KEY,
29+
checkpoints
30+
)
31+
vscode.window.showInformationMessage('Checkpoint description updated.')
32+
}
33+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import * as vscode from 'vscode'
2+
import { CHECKPOINTS_STATE_KEY } from '../../../constants/state-keys'
3+
import type { Checkpoint } from '../types'
4+
import { get_checkpoint_path } from '../utils'
5+
6+
export const get_checkpoints = async (
7+
context: vscode.ExtensionContext
8+
): Promise<Checkpoint[]> => {
9+
const checkpoints =
10+
context.workspaceState.get<Checkpoint[]>(CHECKPOINTS_STATE_KEY, []) ?? []
11+
const valid_checkpoints: Checkpoint[] = []
12+
let state_updated = false
13+
for (const checkpoint of checkpoints) {
14+
try {
15+
const checkpoint_path = get_checkpoint_path(checkpoint.timestamp)
16+
await vscode.workspace.fs.stat(vscode.Uri.file(checkpoint_path))
17+
if (!checkpoint.is_temporary) valid_checkpoints.push(checkpoint)
18+
} catch {
19+
state_updated = true
20+
}
21+
}
22+
23+
if (state_updated) {
24+
await context.workspaceState.update(
25+
CHECKPOINTS_STATE_KEY,
26+
valid_checkpoints
27+
)
28+
}
29+
30+
return valid_checkpoints.sort((a, b) => b.timestamp - a.timestamp)
31+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export * from './copy-workspace-to-dir'
2+
export * from './create-checkpoint'
3+
export * from './create-temporary-checkpoint'
4+
export * from './delete-checkpoint'
5+
export * from './edit-checkpoint'
6+
export * from './get-checkpoints'
7+
export * from './promote-temporary-checkpoint'
8+
export * from './restore-checkpoint'
9+
export * from './sync-workspace-from-dir'

0 commit comments

Comments
 (0)