Skip to content

Commit dca4563

Browse files
authored
Fixes to file search (#1846)
1 parent c7562a6 commit dca4563

File tree

1 file changed

+68
-21
lines changed

1 file changed

+68
-21
lines changed

src/services/search/file-search.ts

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ async function executeRipgrepForFiles(
1515
const args = [
1616
"--files",
1717
"--follow",
18+
"--hidden",
1819
"-g",
1920
"!**/node_modules/**",
2021
"-g",
@@ -32,18 +33,29 @@ async function executeRipgrepForFiles(
3233
crlfDelay: Infinity,
3334
})
3435

35-
const results: { path: string; type: "file" | "folder"; label?: string }[] = []
36+
const fileResults: { path: string; type: "file" | "folder"; label?: string }[] = []
37+
const dirSet = new Set<string>() // Track unique directory paths
3638
let count = 0
3739

3840
rl.on("line", (line) => {
3941
if (count < limit) {
4042
try {
4143
const relativePath = path.relative(workspacePath, line)
42-
results.push({
44+
45+
// Add the file itself
46+
fileResults.push({
4347
path: relativePath,
4448
type: "file",
4549
label: path.basename(relativePath),
4650
})
51+
52+
// Extract and store all parent directory paths
53+
let dirPath = path.dirname(relativePath)
54+
while (dirPath && dirPath !== "." && dirPath !== "/") {
55+
dirSet.add(dirPath)
56+
dirPath = path.dirname(dirPath)
57+
}
58+
4759
count++
4860
} catch (error) {
4961
// Silently ignore errors processing individual paths
@@ -60,10 +72,18 @@ async function executeRipgrepForFiles(
6072
})
6173

6274
rl.on("close", () => {
63-
if (errorOutput && results.length === 0) {
75+
if (errorOutput && fileResults.length === 0) {
6476
reject(new Error(`ripgrep process error: ${errorOutput}`))
6577
} else {
66-
resolve(results)
78+
// Convert directory set to array of directory objects
79+
const dirResults = Array.from(dirSet).map((dirPath) => ({
80+
path: dirPath,
81+
type: "folder" as const,
82+
label: path.basename(dirPath),
83+
}))
84+
85+
// Combine files and directories and resolve
86+
resolve([...fileResults, ...dirResults])
6787
}
6888
})
6989

@@ -86,40 +106,67 @@ export async function searchWorkspaceFiles(
86106
throw new Error("Could not find ripgrep binary")
87107
}
88108

89-
const allFiles = await executeRipgrepForFiles(rgPath, workspacePath, 5000)
109+
// Get all files and directories (from our modified function)
110+
const allItems = await executeRipgrepForFiles(rgPath, workspacePath, 5000)
90111

112+
// If no query, just return the top items
91113
if (!query.trim()) {
92-
return allFiles.slice(0, limit)
114+
return allItems.slice(0, limit)
93115
}
94116

95-
const searchItems = allFiles.map((file) => ({
96-
original: file,
97-
searchStr: `${file.path} ${file.label || ""}`,
117+
// Create search items for all files AND directories
118+
const searchItems = allItems.map((item) => ({
119+
original: item,
120+
searchStr: `${item.path} ${item.label || ""}`,
98121
}))
99122

123+
// Run fzf search on all items
100124
const fzf = new Fzf(searchItems, {
101125
selector: (item) => item.searchStr,
102126
})
103127

104-
const results = fzf
105-
.find(query)
106-
.slice(0, limit)
107-
.map((result) => result.item.original)
128+
// Get all matching results from fzf
129+
const fzfResults = fzf.find(query)
108130

109-
const resultsWithDirectoryCheck = await Promise.all(
110-
results.map(async (result) => {
111-
const fullPath = path.join(workspacePath, result.path)
112-
const isDirectory = fs.existsSync(fullPath) && fs.lstatSync(fullPath).isDirectory()
131+
// First, sort all results by path length (shortest first)
132+
fzfResults.sort((a, b) => {
133+
return a.item.original.path.length - b.item.original.path.length
134+
})
135+
136+
// Take the top N (limit) shortest results
137+
const shortestResults = fzfResults.slice(0, limit).map((result) => result.item.original)
113138

114-
return {
115-
...result,
116-
type: isDirectory ? ("folder" as const) : ("file" as const),
139+
// Verify types of the shortest results
140+
const verifiedResults = await Promise.all(
141+
shortestResults.map(async (result) => {
142+
const fullPath = path.join(workspacePath, result.path)
143+
// Verify if the path exists and is actually a directory
144+
if (fs.existsSync(fullPath)) {
145+
const isDirectory = fs.lstatSync(fullPath).isDirectory()
146+
return {
147+
...result,
148+
type: isDirectory ? ("folder" as const) : ("file" as const),
149+
}
117150
}
151+
// If path doesn't exist, keep original type
152+
return result
118153
}),
119154
)
120155

121-
return resultsWithDirectoryCheck
156+
// Final sort to put directories first within the shortest results
157+
verifiedResults.sort((a, b) => {
158+
if (a.type === "folder" && b.type !== "folder") {
159+
return -1
160+
}
161+
if (a.type !== "folder" && b.type === "folder") {
162+
return 1
163+
}
164+
return 0 // Keep original sorting by path length
165+
})
166+
167+
return verifiedResults
122168
} catch (error) {
169+
console.error("Error in searchWorkspaceFiles:", error)
123170
return []
124171
}
125172
}

0 commit comments

Comments
 (0)