@@ -196,7 +196,8 @@ func (l *LanguageServer) StartDiagnosticsWorker(ctx context.Context) {
196
196
return
197
197
case evt := <- l .diagnosticRequestFile :
198
198
// if file has been deleted, clear diagnostics in the client
199
- if evt .Reason == "textDocument/didDelete" {
199
+ if evt .Reason == "textDocument/didDelete" ||
200
+ evt .Reason == "internal/workspaceStateWorker/missingFile" {
200
201
err := l .sendFileDiagnostics (ctx , evt .URI )
201
202
if err != nil {
202
203
l .logError (fmt .Errorf ("failed to send diagnostic: %w" , err ))
@@ -460,6 +461,72 @@ func (l *LanguageServer) StartCommandWorker(ctx context.Context) {
460
461
}
461
462
}
462
463
464
+ // StartWorkspaceStateWorker will poll for changes to the workspaces state that
465
+ // are not sent from the client. For example, when a file a is removed from the
466
+ // workspace after changing branch.
467
+ func (l * LanguageServer ) StartWorkspaceStateWorker (ctx context.Context ) {
468
+ timer := time .NewTicker (2 * time .Second )
469
+
470
+ for {
471
+ select {
472
+ case <- ctx .Done ():
473
+ return
474
+ case <- timer .C :
475
+ // first clear files that are missing from the workspaceDir
476
+ for fileURI := range l .cache .GetAllFiles () {
477
+ filePath := uri .ToPath (l .clientIdentifier , fileURI )
478
+
479
+ _ , err := os .Stat (filePath )
480
+ if ! os .IsNotExist (err ) {
481
+ // if the file is not missing, we have no work to do
482
+ continue
483
+ }
484
+
485
+ // if the diagnostics for the file are empty, or missing
486
+ // then we do not need to send anything to the client and can
487
+ // remove the file from the cache.
488
+ diagnostics , ok := l .cache .GetFileDiagnostics (fileURI )
489
+ if ! ok || len (diagnostics ) == 0 {
490
+ l .cache .Delete (fileURI )
491
+
492
+ continue
493
+ }
494
+
495
+ // if there are diagnostics, we need to clear them and send a
496
+ // notification to the client.
497
+ l .cache .SetFileDiagnostics (fileURI , []types.Diagnostic {})
498
+ l .diagnosticRequestFile <- fileUpdateEvent {
499
+ URI : fileURI ,
500
+ Reason : "internal/workspaceStateWorker/missingFile" ,
501
+ }
502
+ }
503
+
504
+ // for this next operation, the workspace root must be set as it's
505
+ // used to scan for new files.
506
+ if l .workspaceRootURI == "" {
507
+ continue
508
+ }
509
+
510
+ // next, check if there are any new files that are not ignored and
511
+ // need to be loaded. We get new only so that files being worked
512
+ // on are not loaded from disk during editing.
513
+ changedOrNewURIs , err := l .loadWorkspaceContents (ctx , true )
514
+ if err != nil {
515
+ l .logError (fmt .Errorf ("failed to refresh workspace contents: %w" , err ))
516
+
517
+ continue
518
+ }
519
+
520
+ for _ , uri := range changedOrNewURIs {
521
+ l .diagnosticRequestFile <- fileUpdateEvent {
522
+ URI : uri ,
523
+ Reason : "internal/workspaceStateWorker/changedOrNewFile" ,
524
+ }
525
+ }
526
+ }
527
+ }
528
+ }
529
+
463
530
func (l * LanguageServer ) fixEditParams (
464
531
label string ,
465
532
fix fixes.Fix ,
@@ -1271,7 +1338,7 @@ func (l *LanguageServer) handleWorkspaceDidCreateFiles(
1271
1338
}
1272
1339
1273
1340
for _ , createOp := range params .Files {
1274
- _ , err = cache .UpdateCacheForURIFromDisk (
1341
+ _ , _ , err = cache .UpdateCacheForURIFromDisk (
1275
1342
l .cache ,
1276
1343
uri .FromPath (l .clientIdentifier , createOp .URI ),
1277
1344
uri .ToPath (l .clientIdentifier , createOp .URI ),
@@ -1335,7 +1402,7 @@ func (l *LanguageServer) handleWorkspaceDidRenameFiles(
1335
1402
continue
1336
1403
}
1337
1404
1338
- content , err := cache .UpdateCacheForURIFromDisk (
1405
+ _ , content , err := cache .UpdateCacheForURIFromDisk (
1339
1406
l .cache ,
1340
1407
uri .FromPath (l .clientIdentifier , renameOp .NewURI ),
1341
1408
uri .ToPath (l .clientIdentifier , renameOp .NewURI ),
@@ -1475,7 +1542,7 @@ func (l *LanguageServer) handleInitialize(
1475
1542
l .configWatcher .Watch (configFile .Name ())
1476
1543
}
1477
1544
1478
- err = l .loadWorkspaceContents (ctx )
1545
+ _ , err = l .loadWorkspaceContents (ctx , false )
1479
1546
if err != nil {
1480
1547
return nil , fmt .Errorf ("failed to load workspace contents: %w" , err )
1481
1548
}
@@ -1486,9 +1553,11 @@ func (l *LanguageServer) handleInitialize(
1486
1553
return result , nil
1487
1554
}
1488
1555
1489
- func (l * LanguageServer ) loadWorkspaceContents (ctx context.Context ) error {
1556
+ func (l * LanguageServer ) loadWorkspaceContents (ctx context.Context , newOnly bool ) ([] string , error ) {
1490
1557
workspaceRootPath := uri .ToPath (l .clientIdentifier , l .workspaceRootURI )
1491
1558
1559
+ changedOrNewURIs := make ([]string , 0 )
1560
+
1492
1561
err := filepath .WalkDir (workspaceRootPath , func (path string , d os.DirEntry , err error ) error {
1493
1562
if err != nil {
1494
1563
return fmt .Errorf ("failed to walk workspace dir %q: %w" , path , err )
@@ -1505,23 +1574,36 @@ func (l *LanguageServer) loadWorkspaceContents(ctx context.Context) error {
1505
1574
return nil
1506
1575
}
1507
1576
1508
- _ , err = cache .UpdateCacheForURIFromDisk (l .cache , fileURI , path )
1577
+ // if the caller has requested only new files, then we can exit early
1578
+ if _ , ok := l .cache .GetModule (fileURI ); newOnly && ok {
1579
+ return nil
1580
+ }
1581
+
1582
+ changed , _ , err := cache .UpdateCacheForURIFromDisk (l .cache , fileURI , path )
1509
1583
if err != nil {
1510
1584
return fmt .Errorf ("failed to update cache for uri %q: %w" , path , err )
1511
1585
}
1512
1586
1587
+ // there is no need to update the parse if the file contents
1588
+ // was not changed in the above operation.
1589
+ if ! changed {
1590
+ return nil
1591
+ }
1592
+
1513
1593
_ , err = updateParse (ctx , l .cache , l .regoStore , fileURI )
1514
1594
if err != nil {
1515
1595
return fmt .Errorf ("failed to update parse: %w" , err )
1516
1596
}
1517
1597
1598
+ changedOrNewURIs = append (changedOrNewURIs , fileURI )
1599
+
1518
1600
return nil
1519
1601
})
1520
1602
if err != nil {
1521
- return fmt .Errorf ("failed to walk workspace dir %q: %w" , workspaceRootPath , err )
1603
+ return nil , fmt .Errorf ("failed to walk workspace dir %q: %w" , workspaceRootPath , err )
1522
1604
}
1523
1605
1524
- return nil
1606
+ return changedOrNewURIs , nil
1525
1607
}
1526
1608
1527
1609
func (l * LanguageServer ) handleInitialized (
0 commit comments