@@ -44,6 +44,7 @@ type ShellGlobalsCacheEntry = {
44
44
45
45
type ShellGlobalsCacheEntryWithMeta = ShellGlobalsCacheEntry & { timestamp : number } ;
46
46
const cachedGlobals : Map < string , ShellGlobalsCacheEntryWithMeta > = new Map ( ) ;
47
+ const inflightRequests : Map < string , Promise < ICompletionResource [ ] | undefined > > = new Map ( ) ;
47
48
let pathExecutableCache : PathExecutableCache ;
48
49
const CACHE_KEY = 'terminalSuggestGlobalsCacheV2' ;
49
50
let globalStorageUri : vscode . Uri ;
@@ -122,30 +123,50 @@ async function fetchAndCacheShellGlobals(
122
123
remoteAuthority ?: string ,
123
124
background ?: boolean
124
125
) : Promise < ICompletionResource [ ] | undefined > {
125
- try {
126
- let execShellType = shellType ;
127
- if ( shellType === TerminalShellType . GitBash ) {
128
- execShellType = TerminalShellType . Bash ; // Git Bash is a bash shell
129
- }
130
- const options : ExecOptionsWithStringEncoding = { encoding : 'utf-8' , shell : execShellType , windowsHide : true } ;
131
- const mixedCommands : ( string | ICompletionResource ) [ ] | undefined = await getShellSpecificGlobals . get ( shellType ) ?.( options , existingCommands ) ;
132
- const normalizedCommands = mixedCommands ?. map ( command => typeof command === 'string' ? ( { label : command } ) : command ) ;
133
- if ( machineId ) {
134
- const cacheKey = getCacheKey ( machineId , remoteAuthority , shellType ) ;
135
- cachedGlobals . set ( cacheKey , {
136
- commands : normalizedCommands ,
137
- existingCommands : existingCommands ? Array . from ( existingCommands ) : undefined ,
138
- timestamp : Date . now ( )
139
- } ) ;
140
- await writeGlobalsCache ( ) ;
141
- }
142
- return normalizedCommands ;
143
- } catch ( error ) {
144
- if ( ! background ) {
145
- console . error ( 'Error fetching builtin commands:' , error ) ;
146
- }
147
- return ;
126
+ const cacheKey = getCacheKey ( machineId ?? 'no-machine-id' , remoteAuthority , shellType ) ;
127
+
128
+ // Check if there's already an in-flight request for this cache key
129
+ const existingRequest = inflightRequests . get ( cacheKey ) ;
130
+ if ( existingRequest ) {
131
+ // Wait for the existing request to complete rather than spawning a new process
132
+ return existingRequest ;
148
133
}
134
+
135
+ // Create a new request and store it in the inflight map
136
+ const requestPromise = ( async ( ) => {
137
+ try {
138
+ let execShellType = shellType ;
139
+ if ( shellType === TerminalShellType . GitBash ) {
140
+ execShellType = TerminalShellType . Bash ; // Git Bash is a bash shell
141
+ }
142
+ const options : ExecOptionsWithStringEncoding = { encoding : 'utf-8' , shell : execShellType , windowsHide : true } ;
143
+ const mixedCommands : ( string | ICompletionResource ) [ ] | undefined = await getShellSpecificGlobals . get ( shellType ) ?.( options , existingCommands ) ;
144
+ const normalizedCommands = mixedCommands ?. map ( command => typeof command === 'string' ? ( { label : command } ) : command ) ;
145
+ if ( machineId ) {
146
+ const cacheKey = getCacheKey ( machineId , remoteAuthority , shellType ) ;
147
+ cachedGlobals . set ( cacheKey , {
148
+ commands : normalizedCommands ,
149
+ existingCommands : existingCommands ? Array . from ( existingCommands ) : undefined ,
150
+ timestamp : Date . now ( )
151
+ } ) ;
152
+ await writeGlobalsCache ( ) ;
153
+ }
154
+ return normalizedCommands ;
155
+ } catch ( error ) {
156
+ if ( ! background ) {
157
+ console . error ( 'Error fetching builtin commands:' , error ) ;
158
+ }
159
+ return ;
160
+ } finally {
161
+ // Always remove the promise from inflight requests when done
162
+ inflightRequests . delete ( cacheKey ) ;
163
+ }
164
+ } ) ( ) ;
165
+
166
+ // Store the promise in the inflight map
167
+ inflightRequests . set ( cacheKey , requestPromise ) ;
168
+
169
+ return requestPromise ;
149
170
}
150
171
151
172
0 commit comments