Skip to content

Commit 3e856d7

Browse files
committed
Enhance keyword search with AND logic, exact phrase matching, and workspace-wide search support
1 parent 3b6c2ac commit 3e856d7

5 files changed

Lines changed: 68 additions & 30 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ Meaningful summaries of changes adhering to your preffered style.
112112
- `Code Web Chat: Apply Context` - Apply a saved context or save the current file selection.
113113
- `Code Web Chat: Add File to Context` - Search and add file (or parent folder via file action) to the context.
114114
- `Code Web Chat: Remove File from Context` - Search and remove file (or parent folder via file action) from the context.
115+
- `Code Web Chat: Check All with Keywords` - Search and add files containing specific keywords to the context.
115116
- `Code Web Chat: Copy Context` - Copy XML-formatted checked files from the Workspace view to the clipboard.
116117
- `Code Web Chat: Copy Context of Open Editors` - Copy XML-formatted checked files from the Open Editors view to the clipboard.
117118
- `Code Web Chat: Undo Context Selection` - Undo the last context change.

‎apps/editor/package.json‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -422,10 +422,6 @@
422422
"command": "codeWebChat.findInFolder",
423423
"when": "false"
424424
},
425-
{
426-
"command": "codeWebChat.checkAllWithKeywords",
427-
"when": "false"
428-
},
429425
{
430426
"command": "codeWebChat.revealInExplorer",
431427
"when": "false"

‎apps/editor/src/commands/apply-context-command/sources/check-all-with-keywords-source.ts‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@ export const handle_check_all_with_keywords_source = async (
1515
while (true) {
1616
const input_box = vscode.window.createInputBox()
1717
input_box.title = 'Keyword Search'
18-
input_box.prompt = 'Enter keywords separated by comma'
19-
input_box.placeholder = 'e.g. user, login, auth'
18+
input_box.prompt =
19+
'Enter keywords separated by comma (use & for AND, quotes for whole words)'
20+
input_box.placeholder = 'e.g. user,login&auth,"ignoreFocus"'
2021
input_box.ignoreFocusOut = true
2122
input_box.buttons = [vscode.QuickInputButtons.Back]
2223

@@ -69,8 +70,13 @@ export const handle_check_all_with_keywords_source = async (
6970

7071
const keywords = keywords_input
7172
.split(',')
72-
.map((k) => k.trim().toLowerCase())
73-
.filter((k) => k.length > 0)
73+
.map((group) =>
74+
group
75+
.split('&')
76+
.map((k) => k.trim().toLowerCase())
77+
.filter((k) => k.length > 0)
78+
)
79+
.filter((group) => group.length > 0)
7480

7581
if (keywords.length == 0) continue
7682

‎apps/editor/src/commands/check-all-with-keywords.ts‎

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,14 @@ export const check_all_with_keywords_command = (
1313
return vscode.commands.registerCommand(
1414
'codeWebChat.checkAllWithKeywords',
1515
async (item: any) => {
16-
if (!item || !item.resourceUri) {
17-
return
18-
}
19-
20-
const folder_path = item.resourceUri.fsPath
16+
const folder_path = item?.resourceUri?.fsPath
2117

2218
try {
2319
const input_box = vscode.window.createInputBox()
2420
input_box.title = 'Keyword Search'
25-
input_box.prompt = 'Enter keywords separated by comma'
26-
input_box.placeholder = 'e.g. user, login, auth'
21+
input_box.prompt =
22+
'Enter keywords separated by comma (use & for AND, quotes for whole words)'
23+
input_box.placeholder = 'e.g. user,login&auth,"ignoreFocus"'
2724

2825
const keywords_input = await new Promise<string | undefined>(
2926
(resolve) => {
@@ -63,12 +60,27 @@ export const check_all_with_keywords_command = (
6360

6461
const keywords = keywords_input
6562
.split(',')
66-
.map((k) => k.trim().toLowerCase())
67-
.filter((k) => k.length > 0)
63+
.map((group) =>
64+
group
65+
.split('&')
66+
.map((k) => k.trim().toLowerCase())
67+
.filter((k) => k.length > 0)
68+
)
69+
.filter((group) => group.length > 0)
6870

6971
if (keywords.length == 0) return
7072

71-
const all_files = await workspace_provider.find_all_files(folder_path)
73+
let all_files: string[] = []
74+
75+
if (folder_path) {
76+
all_files = await workspace_provider.find_all_files(folder_path)
77+
} else {
78+
const roots = workspace_provider.get_workspace_roots()
79+
for (const root of roots) {
80+
const files = await workspace_provider.find_all_files(root)
81+
all_files.push(...files)
82+
}
83+
}
7284

7385
const matched_files = await search_files_by_keywords(
7486
all_files,
@@ -158,17 +170,23 @@ export const check_all_with_keywords_command = (
158170
const selected_paths = selected_items.map((item) => item.file_path)
159171
const currently_checked = workspace_provider.get_checked_files()
160172

161-
const files_outside_search_folder = currently_checked.filter((file) => {
162-
const relative = path.relative(folder_path, file)
163-
return relative.startsWith('..') || path.isAbsolute(relative)
164-
})
173+
let files_outside_search_folder: string[] = []
174+
let files_inside_search_folder: string[] = []
165175

166-
let paths_to_apply = [...files_outside_search_folder, ...selected_paths]
176+
if (folder_path) {
177+
files_outside_search_folder = currently_checked.filter((file) => {
178+
const relative = path.relative(folder_path, file)
179+
return relative.startsWith('..') || path.isAbsolute(relative)
180+
})
181+
files_inside_search_folder = currently_checked.filter((file) => {
182+
const relative = path.relative(folder_path, file)
183+
return !relative.startsWith('..') && !path.isAbsolute(relative)
184+
})
185+
} else {
186+
files_inside_search_folder = currently_checked
187+
}
167188

168-
const files_inside_search_folder = currently_checked.filter((file) => {
169-
const relative = path.relative(folder_path, file)
170-
return !relative.startsWith('..') && !path.isAbsolute(relative)
171-
})
189+
let paths_to_apply = [...files_outside_search_folder, ...selected_paths]
172190

173191
if (files_inside_search_folder.length > 0) {
174192
const selected_paths_set = new Set(selected_paths)

‎apps/editor/src/utils/search-files-by-keywords.ts‎

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import * as fs from 'fs'
44

55
export const search_files_by_keywords = async (
66
files: string[],
7-
keywords: string[],
7+
keyword_groups: string[][],
88
progress?: vscode.Progress<{ message?: string; increment?: number }>,
99
token?: vscode.CancellationToken
1010
): Promise<string[]> => {
@@ -13,12 +13,27 @@ export const search_files_by_keywords = async (
1313
let processed = 0
1414
const increment = total > 0 ? (1 / total) * 100 : 0
1515

16+
const matchers = keyword_groups.map((group) =>
17+
group.map((k) => {
18+
if (k.length >= 2 && k.startsWith('"') && k.endsWith('"')) {
19+
const term = k.slice(1, -1)
20+
if (!term) return () => false
21+
const escaped = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
22+
const regex = new RegExp(`\\b${escaped}\\b`)
23+
return (text: string) => regex.test(text)
24+
}
25+
return (text: string) => text.includes(k)
26+
})
27+
)
28+
1629
for (const file_path of files) {
1730
if (token?.isCancellationRequested) break
1831

1932
try {
2033
const file_name_lower = path.basename(file_path).toLowerCase()
21-
if (keywords.some((k) => file_name_lower.includes(k))) {
34+
if (
35+
matchers.some((group) => group.every((match) => match(file_name_lower)))
36+
) {
2237
matched_files.push(file_path)
2338
processed++
2439
progress?.report({
@@ -42,7 +57,9 @@ export const search_files_by_keywords = async (
4257
const content = await fs.promises.readFile(file_path, 'utf-8')
4358
const content_lower = content.toLowerCase()
4459

45-
if (keywords.some((k) => content_lower.includes(k))) {
60+
if (
61+
matchers.some((group) => group.every((match) => match(content_lower)))
62+
) {
4663
matched_files.push(file_path)
4764
}
4865
} catch (error) {

0 commit comments

Comments
 (0)