|
|
@@ -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) => {
|
|
|
- const fullPath = path.join(workspacePath, result.path)
|
|
|
- const isDirectory = fs.existsSync(fullPath) && fs.lstatSync(fullPath).isDirectory()
|
|
|
+ // 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)
|
|
|
|
|
|
- return {
|
|
|
- ...result,
|
|
|
- type: isDirectory ? ("folder" as const) : ("file" as const),
|
|
|
+ // Verify types of the shortest results
|
|
|
+ const verifiedResults = await Promise.all(
|
|
|
+ shortestResults.map(async (result) => {
|
|
|
+ const fullPath = path.join(workspacePath, result.path)
|
|
|
+ // 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 []
|
|
|
}
|
|
|
}
|