@@ -19,17 +19,16 @@ const isWindows = osIsWindows();
19
19
export class PathExecutableCache implements vscode . Disposable {
20
20
private _disposables : vscode . Disposable [ ] = [ ] ;
21
21
22
- private _cachedPathValue : string | undefined ;
23
22
private _cachedWindowsExeExtensions : { [ key : string ] : boolean | undefined } | undefined ;
24
- private _cachedExes : { completionResources : Set < ICompletionResource > | undefined ; labels : Set < string > | undefined } | undefined ;
23
+ private _cachedExes : Map < string , Set < ICompletionResource > | undefined > = new Map ( ) ;
25
24
26
25
constructor ( ) {
27
26
if ( isWindows ) {
28
27
this . _cachedWindowsExeExtensions = vscode . workspace . getConfiguration ( SettingsIds . SuggestPrefix ) . get ( SettingsIds . CachedWindowsExecutableExtensionsSuffixOnly ) ;
29
28
this . _disposables . push ( vscode . workspace . onDidChangeConfiguration ( e => {
30
29
if ( e . affectsConfiguration ( SettingsIds . CachedWindowsExecutableExtensions ) ) {
31
30
this . _cachedWindowsExeExtensions = vscode . workspace . getConfiguration ( SettingsIds . SuggestPrefix ) . get ( SettingsIds . CachedWindowsExecutableExtensionsSuffixOnly ) ;
32
- this . _cachedExes = undefined ;
31
+ this . _cachedExes . clear ( ) ;
33
32
}
34
33
} ) ) ;
35
34
}
@@ -41,9 +40,13 @@ export class PathExecutableCache implements vscode.Disposable {
41
40
}
42
41
}
43
42
44
- refresh ( ) : void {
45
- this . _cachedExes = undefined ;
46
- this . _cachedPathValue = undefined ;
43
+ refresh ( directory ?: string ) : void {
44
+ console . trace ( 'clear cache' ) ;
45
+ if ( directory ) {
46
+ this . _cachedExes . delete ( directory ) ;
47
+ } else {
48
+ this . _cachedExes . clear ( ) ;
49
+ }
47
50
}
48
51
49
52
async getExecutablesInPath ( env : ITerminalEnvironment = process . env , shellType ?: TerminalShellType ) : Promise < { completionResources : Set < ICompletionResource > | undefined ; labels : Set < string > | undefined } | undefined > {
@@ -65,35 +68,56 @@ export class PathExecutableCache implements vscode.Disposable {
65
68
return ;
66
69
}
67
70
68
- // Check cache
69
- if ( this . _cachedExes && this . _cachedPathValue === pathValue ) {
70
- return this . _cachedExes ;
71
- }
72
-
73
71
// Extract executables from PATH
74
72
const paths = pathValue . split ( isWindows ? ';' : ':' ) ;
75
73
const pathSeparator = isWindows ? '\\' : '/' ;
74
+ const promisePaths : string [ ] = [ ] ;
76
75
const promises : Promise < Set < ICompletionResource > | undefined > [ ] = [ ] ;
77
76
const labels : Set < string > = new Set < string > ( ) ;
78
- for ( const path of paths ) {
79
- promises . push ( this . _getExecutablesInPath ( path , pathSeparator , labels ) ) ;
77
+
78
+ for ( const pathDir of paths ) {
79
+ // Check if this directory is already cached
80
+ const cachedExecutables = this . _cachedExes . get ( pathDir ) ;
81
+ if ( cachedExecutables ) {
82
+ for ( const executable of cachedExecutables ) {
83
+ const labelText = typeof executable . label === 'string' ? executable . label : executable . label . label ;
84
+ labels . add ( labelText ) ;
85
+ }
86
+ } else {
87
+ // Not cached, need to scan this directory
88
+ promisePaths . push ( pathDir ) ;
89
+ promises . push ( this . _getExecutablesInPath ( pathDir , pathSeparator , labels ) ) ;
90
+ }
80
91
}
81
92
82
- // Merge all results
93
+ // Process uncached directories
94
+ if ( promises . length > 0 ) {
95
+ const resultSets = await Promise . all ( promises ) ;
96
+ for ( const [ i , resultSet ] of resultSets . entries ( ) ) {
97
+ const pathDir = promisePaths [ i ] ;
98
+ if ( ! this . _cachedExes . has ( pathDir ) ) {
99
+ this . _cachedExes . set ( pathDir , resultSet || new Set ( ) ) ;
100
+ }
101
+ }
102
+ }
103
+
104
+ // Merge all results from all directories
83
105
const executables = new Set < ICompletionResource > ( ) ;
84
- const resultSets = await Promise . all ( promises ) ;
85
- for ( const resultSet of resultSets ) {
86
- if ( resultSet ) {
87
- for ( const executable of resultSet ) {
106
+ const processedPaths : Set < string > = new Set ( ) ;
107
+ for ( const pathDir of paths ) {
108
+ if ( processedPaths . has ( pathDir ) ) {
109
+ continue ;
110
+ }
111
+ processedPaths . add ( pathDir ) ;
112
+ const dirExecutables = this . _cachedExes . get ( pathDir ) ;
113
+ if ( dirExecutables ) {
114
+ for ( const executable of dirExecutables ) {
88
115
executables . add ( executable ) ;
89
116
}
90
117
}
91
118
}
92
119
93
- // Return
94
- this . _cachedPathValue = pathValue ;
95
- this . _cachedExes = { completionResources : executables , labels } ;
96
- return this . _cachedExes ;
120
+ return { completionResources : executables , labels } ;
97
121
}
98
122
99
123
private async _getExecutablesInPath ( path : string , pathSeparator : string , labels : Set < string > ) : Promise < Set < ICompletionResource > | undefined > {
@@ -190,7 +214,7 @@ export async function watchPathDirectories(context: vscode.ExtensionContext, env
190
214
const watcher = filesystem . watch ( dir , { persistent : false } , ( ) => {
191
215
if ( pathExecutableCache ) {
192
216
// Refresh cache when directory contents change
193
- pathExecutableCache . refresh ( ) ;
217
+ pathExecutableCache . refresh ( dir ) ;
194
218
}
195
219
} ) ;
196
220
0 commit comments