Skip to content

Commit f61c75b

Browse files
committed
Add file and folder context management commands with quick pick interfaces
1 parent c8ce7be commit f61c75b

7 files changed

Lines changed: 371 additions & 4 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,8 @@ Generate meaningful summaries of changes adhering to your style.
137137

138138
- `Code Web Chat: Save Context` - Save the currently checked files as a named context for easy reuse.
139139
- `Code Web Chat: Apply Context` - Apply a saved context to either replace or merge with the currently checked files.
140+
- `Code Web Chat: Add File to Context` - Search and add file (or parent folder via file action) to the context.
141+
- `Code Web Chat: Remove File from Context` - Search and remove file (or parent folder via file action) from the context.
140142
- `Code Web Chat: Copy Context` - Copy XML-formatted checked files from the Workspace view to the clipboard.
141143
- `Code Web Chat: Copy Context of Open Editors` - Copy XML-formatted checked files from the Open Editors view to the clipboard.
142144
- `Code Web Chat: Find Paths in Clipboard` - Select files based on paths found in the clipboard text.

packages/vscode/package.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,18 @@
215215
"title": "Code Completion with Instructions using...",
216216
"category": "Code Web Chat"
217217
},
218+
{
219+
"command": "codeWebChat.addFileToContext",
220+
"title": "Add File to Context",
221+
"category": "Code Web Chat",
222+
"icon": "$(add)"
223+
},
224+
{
225+
"command": "codeWebChat.removeFileFromContext",
226+
"title": "Remove File from Context",
227+
"category": "Code Web Chat",
228+
"icon": "$(remove)"
229+
},
218230
{
219231
"command": "codeWebChat.applyChatResponse",
220232
"title": "%codeWebChat.applyChatResponse.title%",
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
import * as vscode from 'vscode'
2+
import * as path from 'path'
3+
import {
4+
WorkspaceProvider,
5+
FileItem
6+
} from '../context/providers/workspace-provider'
7+
import { natural_sort } from '../utils/natural-sort'
8+
9+
interface FileQuickPickItem extends vscode.QuickPickItem {
10+
full_path: string
11+
}
12+
13+
export const add_file_to_context_command = (
14+
workspace_provider: WorkspaceProvider
15+
) => {
16+
return vscode.commands.registerCommand(
17+
'codeWebChat.addFileToContext',
18+
async () => {
19+
const workspace_roots = workspace_provider.getWorkspaceRoots()
20+
if (workspace_roots.length === 0) {
21+
return
22+
}
23+
24+
const quick_pick = vscode.window.createQuickPick<FileQuickPickItem>()
25+
quick_pick.placeholder = 'Select a file to add to context'
26+
quick_pick.matchOnDescription = true
27+
quick_pick.busy = true
28+
quick_pick.show()
29+
30+
quick_pick.onDidTriggerItemButton(async (e) => {
31+
const item = e.item
32+
if (e.button.tooltip == 'Add Parent Folder to Context') {
33+
const workspace_root = workspace_provider.get_workspace_root_for_file(
34+
item.full_path
35+
)
36+
37+
if (!workspace_root) return
38+
39+
const folders: { label: string; full_path: string }[] = []
40+
let current_dir = path.dirname(item.full_path)
41+
42+
while (
43+
current_dir.startsWith(workspace_root) &&
44+
current_dir != workspace_root
45+
) {
46+
const relative = path.relative(workspace_root, current_dir)
47+
folders.push({
48+
label: relative.replace(/\\/g, '/'),
49+
full_path: current_dir
50+
})
51+
current_dir = path.dirname(current_dir)
52+
}
53+
54+
if (folders.length == 0) {
55+
vscode.window.showInformationMessage('No parent folders to add.')
56+
return
57+
}
58+
59+
const folder_quick_pick = vscode.window.createQuickPick<{
60+
label: string
61+
full_path: string
62+
}>()
63+
folder_quick_pick.placeholder = 'Select a folder to add to context'
64+
folder_quick_pick.items = folders.map((f) => ({
65+
label: f.label,
66+
full_path: f.full_path
67+
}))
68+
69+
folder_quick_pick.onDidAccept(async () => {
70+
const selected = folder_quick_pick.selectedItems[0]
71+
if (selected) {
72+
const file_item = new FileItem(
73+
path.basename(selected.full_path),
74+
vscode.Uri.file(selected.full_path),
75+
vscode.TreeItemCollapsibleState.Collapsed,
76+
true,
77+
vscode.TreeItemCheckboxState.Unchecked,
78+
false,
79+
false,
80+
undefined,
81+
undefined,
82+
undefined,
83+
false,
84+
undefined
85+
)
86+
87+
await workspace_provider.update_check_state(
88+
file_item,
89+
vscode.TreeItemCheckboxState.Checked
90+
)
91+
folder_quick_pick.hide()
92+
quick_pick.hide()
93+
}
94+
})
95+
96+
folder_quick_pick.onDidHide(() => {
97+
folder_quick_pick.dispose()
98+
})
99+
100+
folder_quick_pick.show()
101+
}
102+
})
103+
104+
try {
105+
const all_files: string[] = []
106+
for (const root of workspace_roots) {
107+
const files = await workspace_provider.find_all_files(root)
108+
all_files.push(...files)
109+
}
110+
111+
const items: FileQuickPickItem[] = all_files.map((file_path) => {
112+
const workspace_root =
113+
workspace_provider.get_workspace_root_for_file(file_path)
114+
115+
const relative_path = workspace_root
116+
? path.relative(workspace_root, file_path)
117+
: file_path
118+
119+
const filename = path.basename(relative_path)
120+
let directory = path.dirname(relative_path)
121+
if (directory === '.') {
122+
directory = ''
123+
}
124+
125+
if (workspace_roots.length > 1 && workspace_root) {
126+
const workspace_name =
127+
workspace_provider.get_workspace_name(workspace_root)
128+
directory = directory
129+
? `${workspace_name}${directory}`
130+
: workspace_name
131+
}
132+
133+
return {
134+
label: filename,
135+
description: directory,
136+
full_path: file_path,
137+
buttons: [
138+
{
139+
iconPath: new vscode.ThemeIcon('folder'),
140+
tooltip: 'Add Parent Folder to Context'
141+
}
142+
]
143+
}
144+
})
145+
146+
items.sort((a, b) => {
147+
const label_diff = natural_sort(a.label, b.label)
148+
if (label_diff !== 0) return label_diff
149+
return natural_sort(a.description || '', b.description || '')
150+
})
151+
152+
quick_pick.items = items
153+
quick_pick.busy = false
154+
155+
quick_pick.onDidAccept(async () => {
156+
const selected = quick_pick.selectedItems[0]
157+
if (selected) {
158+
const current_checked = workspace_provider.get_checked_files()
159+
if (!current_checked.includes(selected.full_path)) {
160+
await workspace_provider.set_checked_files([
161+
...current_checked,
162+
selected.full_path
163+
])
164+
}
165+
quick_pick.hide()
166+
}
167+
})
168+
169+
quick_pick.onDidHide(() => {
170+
quick_pick.dispose()
171+
})
172+
} catch (error) {
173+
console.error(error)
174+
quick_pick.hide()
175+
}
176+
}
177+
)
178+
}

packages/vscode/src/commands/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
export * from './apply-chat-response-command'
2+
export * from './add-file-to-context-command'
3+
export * from './remove-file-from-context-command'
24
export * from './close-all-editors-command'
35
export * from './checkpoints-command'
46
export * from './close-editor-command'
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import * as vscode from 'vscode'
2+
import * as path from 'path'
3+
import {
4+
WorkspaceProvider,
5+
FileItem
6+
} from '../context/providers/workspace-provider'
7+
import { natural_sort } from '../utils/natural-sort'
8+
9+
interface FileQuickPickItem extends vscode.QuickPickItem {
10+
full_path: string
11+
}
12+
13+
export const remove_file_from_context_command = (
14+
workspace_provider: WorkspaceProvider
15+
) => {
16+
return vscode.commands.registerCommand(
17+
'codeWebChat.removeFileFromContext',
18+
async () => {
19+
const current_checked = workspace_provider.get_checked_files()
20+
21+
if (current_checked.length == 0) {
22+
vscode.window.showInformationMessage('No files currently in context.')
23+
return
24+
}
25+
26+
const quick_pick = vscode.window.createQuickPick<FileQuickPickItem>()
27+
quick_pick.placeholder = 'Select a file to remove from context'
28+
quick_pick.matchOnDescription = true
29+
quick_pick.show()
30+
31+
quick_pick.onDidTriggerItemButton(async (e) => {
32+
const item = e.item
33+
if (e.button.tooltip == 'Remove Parent Folder from Context') {
34+
const workspace_root = workspace_provider.get_workspace_root_for_file(
35+
item.full_path
36+
)
37+
38+
if (!workspace_root) return
39+
40+
const folders: { label: string; full_path: string }[] = []
41+
let current_dir = path.dirname(item.full_path)
42+
43+
while (
44+
current_dir.startsWith(workspace_root) &&
45+
current_dir != workspace_root
46+
) {
47+
const relative = path.relative(workspace_root, current_dir)
48+
folders.push({
49+
label: relative.replace(/\\/g, '/'),
50+
full_path: current_dir
51+
})
52+
current_dir = path.dirname(current_dir)
53+
}
54+
55+
if (folders.length == 0) {
56+
vscode.window.showInformationMessage('No parent folders to remove.')
57+
return
58+
}
59+
60+
const folder_quick_pick = vscode.window.createQuickPick<{
61+
label: string
62+
full_path: string
63+
}>()
64+
folder_quick_pick.placeholder =
65+
'Select a folder to remove from context'
66+
folder_quick_pick.items = folders.map((f) => ({
67+
label: f.label,
68+
full_path: f.full_path
69+
}))
70+
71+
folder_quick_pick.onDidAccept(async () => {
72+
const selected = folder_quick_pick.selectedItems[0]
73+
if (selected) {
74+
const file_item = new FileItem(
75+
path.basename(selected.full_path),
76+
vscode.Uri.file(selected.full_path),
77+
vscode.TreeItemCollapsibleState.Collapsed,
78+
true,
79+
vscode.TreeItemCheckboxState.Checked,
80+
false,
81+
false,
82+
undefined,
83+
undefined,
84+
undefined,
85+
false,
86+
undefined
87+
)
88+
89+
await workspace_provider.update_check_state(
90+
file_item,
91+
vscode.TreeItemCheckboxState.Unchecked
92+
)
93+
folder_quick_pick.hide()
94+
quick_pick.hide()
95+
}
96+
})
97+
98+
folder_quick_pick.onDidHide(() => {
99+
folder_quick_pick.dispose()
100+
})
101+
102+
folder_quick_pick.show()
103+
}
104+
})
105+
106+
const workspace_roots = workspace_provider.getWorkspaceRoots()
107+
108+
const items: FileQuickPickItem[] = current_checked.map((file_path) => {
109+
const workspace_root =
110+
workspace_provider.get_workspace_root_for_file(file_path)
111+
112+
const relative_path = workspace_root
113+
? path.relative(workspace_root, file_path)
114+
: file_path
115+
116+
const filename = path.basename(relative_path)
117+
let directory = path.dirname(relative_path)
118+
if (directory == '.') {
119+
directory = ''
120+
}
121+
122+
if (workspace_roots.length > 1 && workspace_root) {
123+
const workspace_name =
124+
workspace_provider.get_workspace_name(workspace_root)
125+
directory = directory
126+
? `${workspace_name}${directory}`
127+
: workspace_name
128+
}
129+
130+
return {
131+
label: filename,
132+
description: directory,
133+
full_path: file_path,
134+
buttons: [
135+
{
136+
iconPath: new vscode.ThemeIcon('folder'),
137+
tooltip: 'Remove Parent Folder from Context'
138+
}
139+
]
140+
}
141+
})
142+
143+
items.sort((a, b) => {
144+
const label_diff = natural_sort(a.label, b.label)
145+
if (label_diff != 0) return label_diff
146+
return natural_sort(a.description || '', b.description || '')
147+
})
148+
149+
quick_pick.items = items
150+
151+
quick_pick.onDidAccept(async () => {
152+
const selected = quick_pick.selectedItems[0]
153+
if (selected) {
154+
const new_checked = current_checked.filter(
155+
(p) => p != selected.full_path
156+
)
157+
await workspace_provider.set_checked_files(new_checked)
158+
quick_pick.hide()
159+
}
160+
})
161+
162+
quick_pick.onDidHide(() => {
163+
quick_pick.dispose()
164+
})
165+
}
166+
)
167+
}

packages/vscode/src/extension.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import {
99
} from './migrations'
1010
import {
1111
apply_chat_response_command,
12+
add_file_to_context_command,
13+
remove_file_from_context_command,
1214
code_completion_commands,
1315
close_editor_command,
1416
find_paths_in_clipboard_command,
@@ -112,6 +114,8 @@ export async function activate(context: vscode.ExtensionContext) {
112114
rename_command(),
113115
delete_command(),
114116
save_context_command(workspace_provider, context),
117+
add_file_to_context_command(workspace_provider),
118+
remove_file_from_context_command(workspace_provider),
115119
set_range_command(workspace_provider, context),
116120
find_paths_in_clipboard_command(workspace_provider),
117121
duplicate_workspace_command(workspace_provider, websites_provider, context),

0 commit comments

Comments
 (0)