@@ -22,32 +22,40 @@ import * as path from "path";
2222
2323export class LogtalkCallHierarchyProvider implements CallHierarchyProvider {
2424 private logger = getLogger ( ) ;
25- private disposables : Disposable [ ] = [ ] ;
25+ private static readonly TEMP_FILES = [
26+ ".vscode_callers" ,
27+ ".vscode_callers_done" ,
28+ ".vscode_callees" ,
29+ ".vscode_callees_done"
30+ ] ;
31+ private static startupCleanupPromise : Promise < void > | null = null ;
32+ private static workspaceFoldersListener : Disposable | null = null ;
2633
2734 constructor ( ) {
28- // Delete any temporary files from previous sessions in all workspace folders
29- const files = [
30- ".vscode_callers" ,
31- ".vscode_callers_done" ,
32- ".vscode_callees" ,
33- ".vscode_callees_done"
34- ] ;
35- // Fire-and-forget cleanup - errors are logged internally
36- if ( workspace . workspaceFolders ) {
37- for ( const wf of workspace . workspaceFolders ) {
38- Utils . cleanupTemporaryFiles ( wf . uri . fsPath , files ) ;
39- }
35+ // Ensure startup marker cleanup runs once and can be awaited before lookups.
36+ if ( ! LogtalkCallHierarchyProvider . startupCleanupPromise ) {
37+ LogtalkCallHierarchyProvider . startupCleanupPromise = this . cleanupStartupTemporaryFiles ( ) ;
4038 }
4139
42- // Clean up any temporary files when folders are added to the workspace
43- const workspaceFoldersListener = workspace . onDidChangeWorkspaceFolders ( ( event ) => {
44- // For each added workspace folder, run the cleanup using the folder path
45- // Fire-and-forget cleanup - errors are logged internally
46- for ( const wf of event . added ) {
47- Utils . cleanupTemporaryFiles ( wf . uri . fsPath , files ) ;
48- }
49- } ) ;
50- this . disposables . push ( workspaceFoldersListener ) ;
40+ // Register the workspace-folder listener only once to avoid duplicate cleanup runs.
41+ if ( ! LogtalkCallHierarchyProvider . workspaceFoldersListener ) {
42+ LogtalkCallHierarchyProvider . workspaceFoldersListener = workspace . onDidChangeWorkspaceFolders ( ( event ) => {
43+ for ( const wf of event . added ) {
44+ // Fire-and-forget for newly added folders; startup is the critical synchronized path.
45+ Utils . cleanupTemporaryFiles ( wf . uri . fsPath , LogtalkCallHierarchyProvider . TEMP_FILES ) ;
46+ }
47+ } ) ;
48+ }
49+ }
50+
51+ private async cleanupStartupTemporaryFiles ( ) : Promise < void > {
52+ if ( ! workspace . workspaceFolders ) {
53+ return ;
54+ }
55+
56+ for ( const wf of workspace . workspaceFolders ) {
57+ await Utils . cleanupTemporaryFiles ( wf . uri . fsPath , LogtalkCallHierarchyProvider . TEMP_FILES ) ;
58+ }
5159 }
5260
5361 /**
@@ -154,6 +162,7 @@ export class LogtalkCallHierarchyProvider implements CallHierarchyProvider {
154162 position : Position ,
155163 token : CancellationToken
156164 ) : ProviderResult < CallHierarchyItem > {
165+ // prepareCallHierarchy is synchronous by API contract, so we cannot await here.
157166 let predicate = Utils . getCallUnderCursor ( doc , position ) ;
158167 if ( ! predicate ) {
159168 return null ;
@@ -194,6 +203,10 @@ export class LogtalkCallHierarchyProvider implements CallHierarchyProvider {
194203 item : CallHierarchyItem ,
195204 token : CancellationToken
196205 ) : Promise < CallHierarchyIncomingCall [ ] > {
206+ if ( LogtalkCallHierarchyProvider . startupCleanupPromise ) {
207+ await LogtalkCallHierarchyProvider . startupCleanupPromise ;
208+ }
209+
197210 let callers : CallHierarchyIncomingCall [ ] = [ ] ;
198211 let file = item . uri . fsPath ;
199212 let predicate = item . name ;
@@ -250,6 +263,10 @@ export class LogtalkCallHierarchyProvider implements CallHierarchyProvider {
250263 item : CallHierarchyItem ,
251264 token : CancellationToken
252265 ) : Promise < CallHierarchyOutgoingCall [ ] > {
266+ if ( LogtalkCallHierarchyProvider . startupCleanupPromise ) {
267+ await LogtalkCallHierarchyProvider . startupCleanupPromise ;
268+ }
269+
253270 let callees : CallHierarchyOutgoingCall [ ] = [ ] ;
254271 let file = item . uri . fsPath ;
255272 let predicate = item . name ;
@@ -303,13 +320,6 @@ export class LogtalkCallHierarchyProvider implements CallHierarchyProvider {
303320 }
304321
305322 public dispose ( ) : void {
306- for ( const d of this . disposables ) {
307- try {
308- d . dispose ( ) ;
309- } catch ( err ) {
310- this . logger . error ( 'Error disposing resource:' , err ) ;
311- }
312- }
313- this . disposables = [ ] ;
323+ // Shared listener is intentionally process-scoped and initialized once.
314324 }
315325}
0 commit comments