@@ -18,6 +18,7 @@ import {
18
18
getDirectoryPath ,
19
19
getFallbackOptions ,
20
20
getNormalizedAbsolutePath ,
21
+ getRelativePathFromDirectory ,
21
22
getRelativePathToDirectoryOrUrl ,
22
23
getRootLength ,
23
24
getStringComparer ,
@@ -607,15 +608,17 @@ function createDirectoryWatcherSupportingRecursive({
607
608
watcher : FileWatcher ;
608
609
childWatches : ChildWatches ;
609
610
refCount : number ;
611
+ targetWatcher : ChildDirectoryWatcher | undefined ;
612
+ links : Set < string > | undefined ;
610
613
}
611
614
612
- const cache = new Map < string , HostDirectoryWatcher > ( ) ;
615
+ const cache = new Map < Path , HostDirectoryWatcher > ( ) ;
613
616
const callbackCache = createMultiMap < Path , { dirName : string ; callback : DirectoryWatcherCallback ; } > ( ) ;
614
617
const cacheToUpdateChildWatches = new Map < Path , { dirName : string ; options : WatchOptions | undefined ; fileNames : string [ ] ; } > ( ) ;
615
618
let timerToUpdateChildWatches : any ;
616
619
617
620
const filePathComparer = getStringComparer ( ! useCaseSensitiveFileNames ) ;
618
- const toCanonicalFilePath = createGetCanonicalFileName ( useCaseSensitiveFileNames ) ;
621
+ const toCanonicalFilePath = createGetCanonicalFileName ( useCaseSensitiveFileNames ) as ( fileName : string ) => Path ;
619
622
620
623
return ( dirName , callback , recursive , options ) =>
621
624
recursive ?
@@ -625,8 +628,13 @@ function createDirectoryWatcherSupportingRecursive({
625
628
/**
626
629
* Create the directory watcher for the dirPath.
627
630
*/
628
- function createDirectoryWatcher ( dirName : string , options : WatchOptions | undefined , callback ?: DirectoryWatcherCallback ) : ChildDirectoryWatcher {
629
- const dirPath = toCanonicalFilePath ( dirName ) as Path ;
631
+ function createDirectoryWatcher (
632
+ dirName : string ,
633
+ options : WatchOptions | undefined ,
634
+ callback ?: DirectoryWatcherCallback ,
635
+ link ?: string ,
636
+ ) : ChildDirectoryWatcher {
637
+ const dirPath = toCanonicalFilePath ( dirName ) ;
630
638
let directoryWatcher = cache . get ( dirPath ) ;
631
639
if ( directoryWatcher ) {
632
640
directoryWatcher . refCount ++ ;
@@ -640,7 +648,7 @@ function createDirectoryWatcherSupportingRecursive({
640
648
641
649
if ( options ?. synchronousWatchDirectory ) {
642
650
// Call the actual callback
643
- invokeCallbacks ( dirPath , fileName ) ;
651
+ if ( ! cache . get ( dirPath ) ?. targetWatcher ) invokeCallbacks ( dirName , dirPath , fileName ) ;
644
652
645
653
// Iterate through existing children and update the watches if needed
646
654
updateChildWatches ( dirName , dirPath , options ) ;
@@ -654,11 +662,15 @@ function createDirectoryWatcherSupportingRecursive({
654
662
) ,
655
663
refCount : 1 ,
656
664
childWatches : emptyArray ,
665
+ targetWatcher : undefined ,
666
+ links : undefined ,
657
667
} ;
658
668
cache . set ( dirPath , directoryWatcher ) ;
659
669
updateChildWatches ( dirName , dirPath , options ) ;
660
670
}
661
671
672
+ if ( link ) ( directoryWatcher . links ??= new Set ( ) ) . add ( link ) ;
673
+
662
674
const callbackToAdd = callback && { dirName, callback } ;
663
675
if ( callbackToAdd ) {
664
676
callbackCache . add ( dirPath , callbackToAdd ) ;
@@ -669,21 +681,24 @@ function createDirectoryWatcherSupportingRecursive({
669
681
close : ( ) => {
670
682
const directoryWatcher = Debug . checkDefined ( cache . get ( dirPath ) ) ;
671
683
if ( callbackToAdd ) callbackCache . remove ( dirPath , callbackToAdd ) ;
684
+ if ( link ) directoryWatcher . links ?. delete ( link ) ;
672
685
directoryWatcher . refCount -- ;
673
686
674
687
if ( directoryWatcher . refCount ) return ;
675
688
676
689
cache . delete ( dirPath ) ;
690
+ directoryWatcher . links = undefined ;
677
691
closeFileWatcherOf ( directoryWatcher ) ;
692
+ closeTargetWatcher ( directoryWatcher ) ;
678
693
directoryWatcher . childWatches . forEach ( closeFileWatcher ) ;
679
694
} ,
680
695
} ;
681
696
}
682
697
683
698
type InvokeMap = Map < Path , string [ ] | true > ;
684
- function invokeCallbacks ( dirPath : Path , fileName : string ) : void ;
685
- function invokeCallbacks ( dirPath : Path , invokeMap : InvokeMap , fileNames : string [ ] | undefined ) : void ;
686
- function invokeCallbacks ( dirPath : Path , fileNameOrInvokeMap : string | InvokeMap , fileNames ?: string [ ] ) {
699
+ function invokeCallbacks ( dirName : string , dirPath : Path , fileName : string ) : void ;
700
+ function invokeCallbacks ( dirName : string , dirPath : Path , invokeMap : InvokeMap , fileNames : string [ ] | undefined ) : void ;
701
+ function invokeCallbacks ( dirName : string , dirPath : Path , fileNameOrInvokeMap : string | InvokeMap , fileNames ?: string [ ] ) {
687
702
let fileName : string | undefined ;
688
703
let invokeMap : InvokeMap | undefined ;
689
704
if ( isString ( fileNameOrInvokeMap ) ) {
@@ -715,6 +730,15 @@ function createDirectoryWatcherSupportingRecursive({
715
730
}
716
731
}
717
732
} ) ;
733
+ cache . get ( dirPath ) ?. links ?. forEach ( link => {
734
+ const toPathInLink = ( fileName : string ) => combinePaths ( link , getRelativePathFromDirectory ( dirName , fileName , toCanonicalFilePath ) ) ;
735
+ if ( invokeMap ) {
736
+ invokeCallbacks ( link , toCanonicalFilePath ( link ) , invokeMap , fileNames ?. map ( toPathInLink ) ) ;
737
+ }
738
+ else {
739
+ invokeCallbacks ( link , toCanonicalFilePath ( link ) , toPathInLink ( fileName ! ) ) ;
740
+ }
741
+ } ) ;
718
742
}
719
743
720
744
function nonSyncUpdateChildWatches ( dirName : string , dirPath : Path , fileName : string , options : WatchOptions | undefined ) {
@@ -727,7 +751,8 @@ function createDirectoryWatcherSupportingRecursive({
727
751
}
728
752
729
753
// Call the actual callbacks and remove child watches
730
- invokeCallbacks ( dirPath , fileName ) ;
754
+ invokeCallbacks ( dirName , dirPath , fileName ) ;
755
+ closeTargetWatcher ( parentWatcher ) ;
731
756
removeChildWatches ( parentWatcher ) ;
732
757
}
733
758
@@ -760,7 +785,7 @@ function createDirectoryWatcherSupportingRecursive({
760
785
// Because the child refresh is fresh, we would need to invalidate whole root directory being watched
761
786
// to ensure that all the changes are reflected at this time
762
787
const hasChanges = updateChildWatches ( dirName , dirPath , options ) ;
763
- invokeCallbacks ( dirPath , invokeMap , hasChanges ? undefined : fileNames ) ;
788
+ if ( ! cache . get ( dirPath ) ?. targetWatcher ) invokeCallbacks ( dirName , dirPath , invokeMap , hasChanges ? undefined : fileNames ) ;
764
789
}
765
790
766
791
sysLog ( `sysLog:: invokingWatchers:: Elapsed:: ${ timestamp ( ) - start } ms:: ${ cacheToUpdateChildWatches . size } ` ) ;
@@ -792,24 +817,46 @@ function createDirectoryWatcherSupportingRecursive({
792
817
}
793
818
}
794
819
820
+ function closeTargetWatcher ( watcher : HostDirectoryWatcher | undefined ) {
821
+ if ( watcher ?. targetWatcher ) {
822
+ watcher . targetWatcher . close ( ) ;
823
+ watcher . targetWatcher = undefined ;
824
+ }
825
+ }
826
+
795
827
function updateChildWatches ( parentDir : string , parentDirPath : Path , options : WatchOptions | undefined ) {
796
828
// Iterate through existing children and update the watches if needed
797
829
const parentWatcher = cache . get ( parentDirPath ) ;
798
830
if ( ! parentWatcher ) return false ;
831
+ const target = normalizePath ( realpath ( parentDir ) ) ;
832
+ let hasChanges ;
799
833
let newChildWatches : ChildDirectoryWatcher [ ] | undefined ;
800
- const hasChanges = enumerateInsertsAndDeletes < string , ChildDirectoryWatcher > (
801
- fileSystemEntryExists ( parentDir , FileSystemEntryKind . Directory ) ? mapDefined ( getAccessibleSortedChildDirectories ( parentDir ) , child => {
802
- const childFullName = getNormalizedAbsolutePath ( child , parentDir ) ;
803
- // Filter our the symbolic link directories since those arent included in recursive watch
804
- // which is same behaviour when recursive: true is passed to fs.watch
805
- return ! isIgnoredPath ( childFullName , options ) && filePathComparer ( childFullName , normalizePath ( realpath ( childFullName ) ) ) === Comparison . EqualTo ? childFullName : undefined ;
806
- } ) : emptyArray ,
807
- parentWatcher . childWatches ,
808
- ( child , childWatcher ) => filePathComparer ( child , childWatcher . dirName ) ,
809
- createAndAddChildDirectoryWatcher ,
810
- closeFileWatcher ,
811
- addChildDirectoryWatcher ,
812
- ) ;
834
+ if ( filePathComparer ( target , parentDir ) === Comparison . EqualTo ) {
835
+ // if (parentWatcher.target) closeFileWatcher
836
+ hasChanges = enumerateInsertsAndDeletes < string , ChildDirectoryWatcher > (
837
+ fileSystemEntryExists ( parentDir , FileSystemEntryKind . Directory ) ? mapDefined ( getAccessibleSortedChildDirectories ( parentDir ) , child => {
838
+ const childFullName = getNormalizedAbsolutePath ( child , parentDir ) ;
839
+ // Filter our the symbolic link directories since those arent included in recursive watch
840
+ // which is same behaviour when recursive: true is passed to fs.watch
841
+ return ! isIgnoredPath ( childFullName , options ) && filePathComparer ( childFullName , normalizePath ( realpath ( childFullName ) ) ) === Comparison . EqualTo ? childFullName : undefined ;
842
+ } ) : emptyArray ,
843
+ parentWatcher . childWatches ,
844
+ ( child , childWatcher ) => filePathComparer ( child , childWatcher . dirName ) ,
845
+ createAndAddChildDirectoryWatcher ,
846
+ closeFileWatcher ,
847
+ addChildDirectoryWatcher ,
848
+ ) ;
849
+ }
850
+ else if ( parentWatcher . targetWatcher && filePathComparer ( target , parentWatcher . targetWatcher . dirName ) === Comparison . EqualTo ) {
851
+ hasChanges = false ;
852
+ Debug . assert ( parentWatcher . childWatches === emptyArray ) ;
853
+ }
854
+ else {
855
+ closeTargetWatcher ( parentWatcher ) ;
856
+ parentWatcher . targetWatcher = createDirectoryWatcher ( target , options , /*callback*/ undefined , parentDir ) ;
857
+ parentWatcher . childWatches . forEach ( closeFileWatcher ) ;
858
+ hasChanges = true ;
859
+ }
813
860
parentWatcher . childWatches = newChildWatches || emptyArray ;
814
861
return hasChanges ;
815
862
0 commit comments