@@ -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