@@ -73,12 +73,24 @@ define(function (require, exports, module) {
73
73
var searchModel = new SearchModel ( ) ;
74
74
75
75
/* Forward declarations */
76
- var _documentChangeHandler , _fileSystemChangeHandler , _fileNameChangeHandler , clearSearch ;
76
+ var _documentChangeHandler ,
77
+ _fileSystemChangeHandler ,
78
+ _processCachedFileSystemEvents ,
79
+ _debouncedFileSystemChangeHandler ,
80
+ _fileNameChangeHandler ,
81
+ clearSearch ;
82
+
83
+ /**
84
+ * Waits for FS changes to stack up until processing them
85
+ * (scripts like npm install can do a lot of movements on the disk)
86
+ * @const
87
+ */
88
+ var FILE_SYSTEM_EVENT_DEBOUNCE_TIME = 100 ;
77
89
78
90
/** Remove the listeners that were tracking potential search result changes */
79
91
function _removeListeners ( ) {
80
92
DocumentModule . off ( "documentChange" , _documentChangeHandler ) ;
81
- FileSystem . off ( "change" , _fileSystemChangeHandler ) ;
93
+ FileSystem . off ( "change" , _debouncedFileSystemChangeHandler ) ;
82
94
DocumentManager . off ( "fileNameChange" , _fileNameChangeHandler ) ;
83
95
}
84
96
@@ -88,7 +100,7 @@ define(function (require, exports, module) {
88
100
_removeListeners ( ) ;
89
101
90
102
DocumentModule . on ( "documentChange" , _documentChangeHandler ) ;
91
- FileSystem . on ( "change" , _fileSystemChangeHandler ) ;
103
+ FileSystem . on ( "change" , _debouncedFileSystemChangeHandler ) ;
92
104
DocumentManager . on ( "fileNameChange" , _fileNameChangeHandler ) ;
93
105
}
94
106
@@ -658,7 +670,7 @@ define(function (require, exports, module) {
658
670
* @param {array } fileList The list of files that changed.
659
671
*/
660
672
function filesChanged ( fileList ) {
661
- if ( FindUtils . isNodeSearchDisabled ( ) || fileList . length === 0 ) {
673
+ if ( FindUtils . isNodeSearchDisabled ( ) || ! fileList || fileList . length === 0 ) {
662
674
return ;
663
675
}
664
676
var updateObject = {
@@ -676,7 +688,7 @@ define(function (require, exports, module) {
676
688
* @param {array } fileList The list of files that was removed.
677
689
*/
678
690
function filesRemoved ( fileList ) {
679
- if ( FindUtils . isNodeSearchDisabled ( ) ) {
691
+ if ( FindUtils . isNodeSearchDisabled ( ) || ! fileList || fileList . length === 0 ) {
680
692
return ;
681
693
}
682
694
var updateObject = {
@@ -732,69 +744,83 @@ define(function (require, exports, module) {
732
744
733
745
/*
734
746
* Remove existing search results that match the given entry's path
735
- * @param {(File|Directory) } entry
747
+ * @param {Array.< (File|Directory)> } entries
736
748
*/
737
- function _removeSearchResultsForEntry ( entry ) {
738
- Object . keys ( searchModel . results ) . forEach ( function ( fullPath ) {
739
- if ( fullPath === entry . fullPath ||
740
- ( entry . isDirectory && fullPath . indexOf ( entry . fullPath ) === 0 ) ) {
741
- // node search : inform node that the file is removed
742
- filesRemoved ( [ fullPath ] ) ;
743
- if ( findOrReplaceInProgress ) {
744
- searchModel . removeResults ( fullPath ) ;
745
- resultsChanged = true ;
749
+ function _removeSearchResultsForEntries ( entries ) {
750
+ var fullPaths = [ ] ;
751
+ entries . forEach ( function ( entry ) {
752
+ Object . keys ( searchModel . results ) . forEach ( function ( fullPath ) {
753
+ if ( fullPath === entry . fullPath ||
754
+ ( entry . isDirectory && fullPath . indexOf ( entry . fullPath ) === 0 ) ) {
755
+ // node search : inform node that the file is removed
756
+ fullPaths . push ( fullPath ) ;
757
+ if ( findOrReplaceInProgress ) {
758
+ searchModel . removeResults ( fullPath ) ;
759
+ resultsChanged = true ;
760
+ }
746
761
}
747
- }
762
+ } ) ;
748
763
} ) ;
764
+ // this should be called once with a large array instead of numerous calls with single items
765
+ filesRemoved ( fullPaths ) ;
749
766
}
750
767
751
768
/*
752
- * Add new search results for this entry and all of its children
753
- * @param {(File|Directory) } entry
769
+ * Add new search results for these entries and all of its children
770
+ * @param {Array.< (File|Directory)> } entries
754
771
* @return {jQuery.Promise } Resolves when the results have been added
755
772
*/
756
- function _addSearchResultsForEntry ( entry ) {
757
- var addedFiles = [ ] ,
758
- addedFilePaths = [ ] ,
759
- deferred = new $ . Deferred ( ) ;
760
-
761
- // gather up added files
762
- var visitor = function ( child ) {
763
- // Replicate filtering that getAllFiles() does
764
- if ( ProjectManager . shouldShow ( child ) ) {
765
- if ( child . isFile && _isReadableText ( child . name ) ) {
766
- // Re-check the filtering that the initial search applied
767
- if ( _inSearchScope ( child ) ) {
768
- addedFiles . push ( child ) ;
769
- addedFilePaths . push ( child . fullPath ) ;
773
+ function _addSearchResultsForEntries ( entries ) {
774
+ var fullPaths = [ ] ;
775
+ return Async . doInParallel ( entries , function ( entry ) {
776
+ var addedFiles = [ ] ,
777
+ addedFilePaths = [ ] ,
778
+ deferred = new $ . Deferred ( ) ;
779
+
780
+ // gather up added files
781
+ var visitor = function ( child ) {
782
+ // Replicate filtering that getAllFiles() does
783
+ if ( ProjectManager . shouldShow ( child ) ) {
784
+ if ( child . isFile && _isReadableText ( child . name ) ) {
785
+ // Re-check the filtering that the initial search applied
786
+ if ( _inSearchScope ( child ) ) {
787
+ addedFiles . push ( child ) ;
788
+ addedFilePaths . push ( child . fullPath ) ;
789
+ }
770
790
}
791
+ return true ;
771
792
}
772
- return true ;
773
- }
774
- return false ;
775
- } ;
793
+ return false ;
794
+ } ;
776
795
777
- entry . visit ( visitor , function ( err ) {
778
- if ( err ) {
779
- deferred . reject ( err ) ;
780
- return ;
781
- }
796
+ entry . visit ( visitor , function ( err ) {
797
+ if ( err ) {
798
+ deferred . reject ( err ) ;
799
+ return ;
800
+ }
782
801
783
- //node Search : inform node about the file changes
784
- filesChanged ( addedFilePaths ) ;
802
+ //node Search : inform node about the file changes
803
+ //filesChanged(addedFilePaths);
804
+ fullPaths = fullPaths . concat ( addedFilePaths ) ;
785
805
786
- if ( findOrReplaceInProgress ) {
787
- // find additional matches in all added files
788
- Async . doInParallel ( addedFiles , function ( file ) {
789
- return _doSearchInOneFile ( file )
790
- . done ( function ( foundMatches ) {
791
- resultsChanged = resultsChanged || foundMatches ;
792
- } ) ;
793
- } ) . always ( deferred . resolve ) ;
794
- }
795
- } ) ;
806
+ if ( findOrReplaceInProgress ) {
807
+ // find additional matches in all added files
808
+ Async . doInParallel ( addedFiles , function ( file ) {
809
+ return _doSearchInOneFile ( file )
810
+ . done ( function ( foundMatches ) {
811
+ resultsChanged = resultsChanged || foundMatches ;
812
+ } ) ;
813
+ } ) . always ( deferred . resolve ) ;
814
+ } else {
815
+ deferred . resolve ( ) ;
816
+ }
817
+ } ) ;
796
818
797
- return deferred . promise ( ) ;
819
+ return deferred . promise ( ) ;
820
+ } ) . always ( function ( ) {
821
+ // this should be called once with a large array instead of numerous calls with single items
822
+ filesChanged ( fullPaths ) ;
823
+ } ) ;
798
824
}
799
825
800
826
if ( ! entry ) {
@@ -804,23 +830,23 @@ define(function (require, exports, module) {
804
830
805
831
var addPromise ;
806
832
if ( entry . isDirectory ) {
807
- if ( ! added || ! removed || ( added . length === 0 && removed . length === 0 ) ) {
833
+ if ( added . length === 0 && removed . length === 0 ) {
808
834
// If the added or removed sets are null, must redo the search for the entire subtree - we
809
835
// don't know which child files/folders may have been added or removed.
810
- _removeSearchResultsForEntry ( entry ) ;
836
+ _removeSearchResultsForEntries ( [ entry ] ) ;
811
837
812
838
var deferred = $ . Deferred ( ) ;
813
839
addPromise = deferred . promise ( ) ;
814
840
entry . getContents ( function ( err , entries ) {
815
- Async . doInParallel ( entries , _addSearchResultsForEntry ) . always ( deferred . resolve ) ;
841
+ _addSearchResultsForEntries ( entries ) . always ( deferred . resolve ) ;
816
842
} ) ;
817
843
} else {
818
- removed . forEach ( _removeSearchResultsForEntry ) ;
819
- addPromise = Async . doInParallel ( added , _addSearchResultsForEntry ) ;
844
+ _removeSearchResultsForEntries ( removed ) ;
845
+ addPromise = _addSearchResultsForEntries ( added ) ;
820
846
}
821
847
} else { // entry.isFile
822
- _removeSearchResultsForEntry ( entry ) ;
823
- addPromise = _addSearchResultsForEntry ( entry ) ;
848
+ _removeSearchResultsForEntries ( [ entry ] ) ;
849
+ addPromise = _addSearchResultsForEntries ( [ entry ] ) ;
824
850
}
825
851
826
852
addPromise . always ( function ( ) {
@@ -830,6 +856,56 @@ define(function (require, exports, module) {
830
856
}
831
857
} ) ;
832
858
} ;
859
+
860
+ /**
861
+ * This stores file system events emitted by watchers that were not yet processed
862
+ */
863
+ var _cachedFileSystemEvents = [ ] ;
864
+
865
+ /**
866
+ * Debounced function to process emitted file system events
867
+ * for cases when there's a lot of fs events emitted in a very short period of time
868
+ */
869
+ _processCachedFileSystemEvents = _ . debounce ( function ( ) {
870
+ // we need to reduce _cachedFileSystemEvents not to contain duplicates!
871
+ _cachedFileSystemEvents = _cachedFileSystemEvents . reduce ( function ( result , obj ) {
872
+ var fullPath = obj . entry ? obj . entry . fullPath : null ;
873
+ // merge added & removed
874
+ if ( result [ fullPath ] && obj . isDirectory ) {
875
+ obj . added = obj . added . concat ( result [ fullPath ] . added ) ;
876
+ obj . removed = obj . removed . concat ( result [ fullPath ] . removed ) ;
877
+ }
878
+ // use the latest event as base
879
+ result [ fullPath ] = obj ;
880
+ return result ;
881
+ } , { } ) ;
882
+ _ . forEach ( _cachedFileSystemEvents , function ( obj ) {
883
+ _fileSystemChangeHandler ( obj . event , obj . entry , obj . added , obj . removed ) ;
884
+ } ) ;
885
+ _cachedFileSystemEvents = [ ] ;
886
+ } , FILE_SYSTEM_EVENT_DEBOUNCE_TIME ) ;
887
+
888
+ /**
889
+ * Wrapper function for _fileSystemChangeHandler which handles all incoming fs events
890
+ * putting them to cache and executing a debounced function
891
+ */
892
+ _debouncedFileSystemChangeHandler = function ( event , entry , added , removed ) {
893
+ // normalize this here so we don't need to handle null later
894
+ var isDirectory = false ;
895
+ if ( entry && entry . isDirectory ) {
896
+ isDirectory = true ;
897
+ added = added || [ ] ;
898
+ removed = removed || [ ] ;
899
+ }
900
+ _cachedFileSystemEvents . push ( {
901
+ event : event ,
902
+ entry : entry ,
903
+ isDirectory : isDirectory ,
904
+ added : added ,
905
+ removed : removed
906
+ } ) ;
907
+ _processCachedFileSystemEvents ( ) ;
908
+ } ;
833
909
834
910
/**
835
911
* On project change, inform node about the new list of files that needs to be crawled.
0 commit comments