Skip to content

Commit d87680a

Browse files
committed
Merge remote-tracking branch 'adobe/master'
2 parents 99a92a9 + 94ed8b3 commit d87680a

File tree

1 file changed

+137
-61
lines changed

1 file changed

+137
-61
lines changed

src/search/FindInFiles.js

Lines changed: 137 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,24 @@ define(function (require, exports, module) {
7373
var searchModel = new SearchModel();
7474

7575
/* 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;
7789

7890
/** Remove the listeners that were tracking potential search result changes */
7991
function _removeListeners() {
8092
DocumentModule.off("documentChange", _documentChangeHandler);
81-
FileSystem.off("change", _fileSystemChangeHandler);
93+
FileSystem.off("change", _debouncedFileSystemChangeHandler);
8294
DocumentManager.off("fileNameChange", _fileNameChangeHandler);
8395
}
8496

@@ -88,7 +100,7 @@ define(function (require, exports, module) {
88100
_removeListeners();
89101

90102
DocumentModule.on("documentChange", _documentChangeHandler);
91-
FileSystem.on("change", _fileSystemChangeHandler);
103+
FileSystem.on("change", _debouncedFileSystemChangeHandler);
92104
DocumentManager.on("fileNameChange", _fileNameChangeHandler);
93105
}
94106

@@ -658,7 +670,7 @@ define(function (require, exports, module) {
658670
* @param {array} fileList The list of files that changed.
659671
*/
660672
function filesChanged(fileList) {
661-
if (FindUtils.isNodeSearchDisabled() || fileList.length === 0) {
673+
if (FindUtils.isNodeSearchDisabled() || !fileList || fileList.length === 0) {
662674
return;
663675
}
664676
var updateObject = {
@@ -676,7 +688,7 @@ define(function (require, exports, module) {
676688
* @param {array} fileList The list of files that was removed.
677689
*/
678690
function filesRemoved(fileList) {
679-
if (FindUtils.isNodeSearchDisabled()) {
691+
if (FindUtils.isNodeSearchDisabled() || !fileList || fileList.length === 0) {
680692
return;
681693
}
682694
var updateObject = {
@@ -732,69 +744,83 @@ define(function (require, exports, module) {
732744

733745
/*
734746
* Remove existing search results that match the given entry's path
735-
* @param {(File|Directory)} entry
747+
* @param {Array.<(File|Directory)>} entries
736748
*/
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+
}
746761
}
747-
}
762+
});
748763
});
764+
// this should be called once with a large array instead of numerous calls with single items
765+
filesRemoved(fullPaths);
749766
}
750767

751768
/*
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
754771
* @return {jQuery.Promise} Resolves when the results have been added
755772
*/
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+
}
770790
}
791+
return true;
771792
}
772-
return true;
773-
}
774-
return false;
775-
};
793+
return false;
794+
};
776795

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+
}
782801

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);
785805

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+
});
796818

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+
});
798824
}
799825

800826
if (!entry) {
@@ -804,23 +830,23 @@ define(function (require, exports, module) {
804830

805831
var addPromise;
806832
if (entry.isDirectory) {
807-
if (!added || !removed || (added.length === 0 && removed.length === 0)) {
833+
if (added.length === 0 && removed.length === 0) {
808834
// If the added or removed sets are null, must redo the search for the entire subtree - we
809835
// don't know which child files/folders may have been added or removed.
810-
_removeSearchResultsForEntry(entry);
836+
_removeSearchResultsForEntries([ entry ]);
811837

812838
var deferred = $.Deferred();
813839
addPromise = deferred.promise();
814840
entry.getContents(function (err, entries) {
815-
Async.doInParallel(entries, _addSearchResultsForEntry).always(deferred.resolve);
841+
_addSearchResultsForEntries(entries).always(deferred.resolve);
816842
});
817843
} else {
818-
removed.forEach(_removeSearchResultsForEntry);
819-
addPromise = Async.doInParallel(added, _addSearchResultsForEntry);
844+
_removeSearchResultsForEntries(removed);
845+
addPromise = _addSearchResultsForEntries(added);
820846
}
821847
} else { // entry.isFile
822-
_removeSearchResultsForEntry(entry);
823-
addPromise = _addSearchResultsForEntry(entry);
848+
_removeSearchResultsForEntries([ entry ]);
849+
addPromise = _addSearchResultsForEntries([ entry ]);
824850
}
825851

826852
addPromise.always(function () {
@@ -830,6 +856,56 @@ define(function (require, exports, module) {
830856
}
831857
});
832858
};
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+
};
833909

834910
/**
835911
* On project change, inform node about the new list of files that needs to be crawled.

0 commit comments

Comments
 (0)