@@ -135,6 +135,13 @@ type Session struct {
135135 diagnosticsRefreshCancel context.CancelFunc
136136 diagnosticsRefreshMu sync.Mutex
137137
138+ // warmAutoImportCancel is the cancelation function for a running
139+ // auto-import cache warming task. It is cancelled on file opens,
140+ // closes, changes, watched-file changes, new auto-import warming
141+ // requests, and when the session closes.
142+ warmAutoImportCancel context.CancelFunc
143+ warmAutoImportMu sync.Mutex
144+
138145 // idleCacheCleanTimer is a resettable timer for scheduling idle disk
139146 // cache cleans. The timer resets on any file event (open, close,
140147 // change, save, watch) and fires after 30 seconds of inactivity.
@@ -270,6 +277,7 @@ func (s *Session) InitializeWithUserConfig(config lsutil.UserPreferences) {
270277
271278func (s * Session ) DidOpenFile (ctx context.Context , uri lsproto.DocumentUri , version int32 , content string , languageKind lsproto.LanguageKind ) {
272279 s .cancelDiagnosticsRefresh ()
280+ s .cancelWarmAutoImportCache ()
273281 s .scheduleIdleCacheClean ()
274282 s .snapshotUpdateMu .Lock ()
275283 defer s .snapshotUpdateMu .Unlock ()
@@ -294,6 +302,7 @@ func (s *Session) DidOpenFile(ctx context.Context, uri lsproto.DocumentUri, vers
294302
295303func (s * Session ) DidCloseFile (ctx context.Context , uri lsproto.DocumentUri ) {
296304 s .cancelDiagnosticsRefresh ()
305+ s .cancelWarmAutoImportCache ()
297306 s .scheduleIdleCacheClean ()
298307 s .pendingFileChangesMu .Lock ()
299308 defer s .pendingFileChangesMu .Unlock ()
@@ -305,6 +314,7 @@ func (s *Session) DidCloseFile(ctx context.Context, uri lsproto.DocumentUri) {
305314
306315func (s * Session ) DidChangeFile (ctx context.Context , uri lsproto.DocumentUri , version int32 , changes []lsproto.TextDocumentContentChangePartialOrWholeDocument ) {
307316 s .cancelDiagnosticsRefresh ()
317+ s .cancelWarmAutoImportCache ()
308318 s .scheduleIdleCacheClean ()
309319 s .pendingFileChangesMu .Lock ()
310320 defer s .pendingFileChangesMu .Unlock ()
@@ -353,6 +363,7 @@ func (s *Session) DidChangeWatchedFiles(ctx context.Context, changes []*lsproto.
353363
354364 // Schedule a debounced diagnostics refresh
355365 s .ScheduleDiagnosticsRefresh ()
366+ s .cancelWarmAutoImportCache ()
356367 s .scheduleIdleCacheClean ()
357368}
358369
@@ -416,6 +427,15 @@ func (s *Session) cancelDiagnosticsRefresh() {
416427 }
417428}
418429
430+ func (s * Session ) cancelWarmAutoImportCache () {
431+ s .warmAutoImportMu .Lock ()
432+ defer s .warmAutoImportMu .Unlock ()
433+ if s .warmAutoImportCancel != nil {
434+ s .warmAutoImportCancel ()
435+ s .warmAutoImportCancel = nil
436+ }
437+ }
438+
419439const idleCacheCleanDelay = 30 * time .Second
420440
421441func (s * Session ) scheduleIdleCacheClean () {
@@ -1245,6 +1265,8 @@ func (s *Session) updateWatches(oldSnapshot *Snapshot, newSnapshot *Snapshot) er
12451265func (s * Session ) Close () {
12461266 // Cancel any pending diagnostics refresh
12471267 s .cancelDiagnosticsRefresh ()
1268+ // Cancel any pending auto-import cache warming
1269+ s .cancelWarmAutoImportCache ()
12481270 // Cancel any pending idle cache clean
12491271 s .cancelIdleCacheClean ()
12501272 // Cancel periodic performance telemetry
@@ -1590,6 +1612,56 @@ func (s *Session) warmAutoImportCache(ctx context.Context, change SnapshotChange
15901612 ) {
15911613 return
15921614 }
1593- _ , _ = s .GetCurrentLanguageServiceWithAutoImports (ctx , changedFile )
1615+
1616+ // Cancel any previous auto-import warming and create a new cancellable context.
1617+ // Only publish the new cancel func if the derived context is still active,
1618+ // and make the stored cancel func a no-op once that warming task is done.
1619+ s .warmAutoImportMu .Lock ()
1620+ if s .warmAutoImportCancel != nil {
1621+ s .warmAutoImportCancel ()
1622+ }
1623+ warmCtx , cancel := context .WithCancel (ctx )
1624+ if warmCtx .Err () == nil {
1625+ s .warmAutoImportCancel = func () {
1626+ if warmCtx .Err () != nil {
1627+ return
1628+ }
1629+ s .logger .Logf ("Cancelling auto-import warming for file %s" , changedFile .FileName ())
1630+ cancel ()
1631+ }
1632+ }
1633+ s .warmAutoImportMu .Unlock ()
1634+
1635+ if warmCtx .Err () != nil {
1636+ cancel ()
1637+ return
1638+ }
1639+ defer cancel ()
1640+
1641+ // Clone the snapshot with auto-imports using warmCtx so the expensive
1642+ // extraction work is cancelled if a file change arrives.
1643+ if ! newSnapshot .tryRef () {
1644+ return
1645+ }
1646+ defer newSnapshot .Deref (s )
1647+
1648+ warmChange := SnapshotChange {
1649+ reason : UpdateReasonRequestedLanguageServiceWithAutoImports ,
1650+ ResourceRequest : ResourceRequest {
1651+ Documents : []lsproto.DocumentUri {changedFile },
1652+ AutoImports : changedFile ,
1653+ },
1654+ }
1655+ clonedSnapshot := newSnapshot .Clone (warmCtx , warmChange , newSnapshot .fs .overlays , s )
1656+
1657+ // If cancelled during clone, discard the incomplete result.
1658+ if warmCtx .Err () != nil {
1659+ clonedSnapshot .Deref (s )
1660+ return
1661+ }
1662+
1663+ // Conditionally adopt: if the session hasn't moved past newSnapshot,
1664+ // promote the clone so future requests benefit from the warmed cache.
1665+ s .adoptSnapshotChange (newSnapshot , clonedSnapshot )
15941666 }
15951667}
0 commit comments