Fixes to file search (#1846)

This commit is contained in:
Matt Rubens 2025-03-20 12:14:32 -04:00 committed by GitHub
parent c7562a6a37
commit dca4563b9a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -15,6 +15,7 @@ async function executeRipgrepForFiles(
const args = [
"--files",
"--follow",
"--hidden",
"-g",
"!**/node_modules/**",
"-g",
@ -32,18 +33,29 @@ async function executeRipgrepForFiles(
crlfDelay: Infinity,
})
const results: { path: string; type: "file" | "folder"; label?: string }[] = []
const fileResults: { path: string; type: "file" | "folder"; label?: string }[] = []
const dirSet = new Set<string>() // Track unique directory paths
let count = 0
rl.on("line", (line) => {
if (count < limit) {
try {
const relativePath = path.relative(workspacePath, line)
results.push({
// Add the file itself
fileResults.push({
path: relativePath,
type: "file",
label: path.basename(relativePath),
})
// Extract and store all parent directory paths
let dirPath = path.dirname(relativePath)
while (dirPath && dirPath !== "." && dirPath !== "/") {
dirSet.add(dirPath)
dirPath = path.dirname(dirPath)
}
count++
} catch (error) {
// Silently ignore errors processing individual paths
@ -60,10 +72,18 @@ async function executeRipgrepForFiles(
})
rl.on("close", () => {
if (errorOutput && results.length === 0) {
if (errorOutput && fileResults.length === 0) {
reject(new Error(`ripgrep process error: ${errorOutput}`))
} else {
resolve(results)
// Convert directory set to array of directory objects
const dirResults = Array.from(dirSet).map((dirPath) => ({
path: dirPath,
type: "folder" as const,
label: path.basename(dirPath),
}))
// Combine files and directories and resolve
resolve([...fileResults, ...dirResults])
}
})
@ -86,40 +106,67 @@ export async function searchWorkspaceFiles(
throw new Error("Could not find ripgrep binary")
}
const allFiles = await executeRipgrepForFiles(rgPath, workspacePath, 5000)
// Get all files and directories (from our modified function)
const allItems = await executeRipgrepForFiles(rgPath, workspacePath, 5000)
// If no query, just return the top items
if (!query.trim()) {
return allFiles.slice(0, limit)
return allItems.slice(0, limit)
}
const searchItems = allFiles.map((file) => ({
original: file,
searchStr: `${file.path} ${file.label || ""}`,
// Create search items for all files AND directories
const searchItems = allItems.map((item) => ({
original: item,
searchStr: `${item.path} ${item.label || ""}`,
}))
// Run fzf search on all items
const fzf = new Fzf(searchItems, {
selector: (item) => item.searchStr,
})
const results = fzf
.find(query)
.slice(0, limit)
.map((result) => result.item.original)
// Get all matching results from fzf
const fzfResults = fzf.find(query)
const resultsWithDirectoryCheck = await Promise.all(
results.map(async (result) => {
// First, sort all results by path length (shortest first)
fzfResults.sort((a, b) => {
return a.item.original.path.length - b.item.original.path.length
})
// Take the top N (limit) shortest results
const shortestResults = fzfResults.slice(0, limit).map((result) => result.item.original)
// Verify types of the shortest results
const verifiedResults = await Promise.all(
shortestResults.map(async (result) => {
const fullPath = path.join(workspacePath, result.path)
const isDirectory = fs.existsSync(fullPath) && fs.lstatSync(fullPath).isDirectory()
return {
...result,
type: isDirectory ? ("folder" as const) : ("file" as const),
// Verify if the path exists and is actually a directory
if (fs.existsSync(fullPath)) {
const isDirectory = fs.lstatSync(fullPath).isDirectory()
return {
...result,
type: isDirectory ? ("folder" as const) : ("file" as const),
}
}
// If path doesn't exist, keep original type
return result
}),
)
return resultsWithDirectoryCheck
// Final sort to put directories first within the shortest results
verifiedResults.sort((a, b) => {
if (a.type === "folder" && b.type !== "folder") {
return -1
}
if (a.type !== "folder" && b.type === "folder") {
return 1
}
return 0 // Keep original sorting by path length
})
return verifiedResults
} catch (error) {
console.error("Error in searchWorkspaceFiles:", error)
return []
}
}