diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index 26e348f72..bfada8b43 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -978,7 +978,8 @@ describe "Workspace", -> ] onFakeSearchCreated = (fakeSearch) -> fakeSearch.options.didMatch(searchResult) - fakeSearch.options.didSearchPaths(numPathsToPretendToSearchInCustomDirectorySearcher) + directory1 = atom.project.getDirectories()[atom.project.getPaths().indexOf(dir1)] + fakeSearch.options.didSearchPaths(directory1, numPathsToPretendToSearchInCustomDirectorySearcher) fakeSearch.hoistedResolve() resultPaths = [] diff --git a/src/default-directory-searcher.coffee b/src/default-directory-searcher.coffee index 5db5db8c2..4767a0313 100644 --- a/src/default-directory-searcher.coffee +++ b/src/default-directory-searcher.coffee @@ -4,9 +4,7 @@ Task = require './task' # # Implements thenable so it can be used with `Promise.all()`. class DirectorySearch - # Public: Creates a new DirectorySearch that will not start running until the - # `emitter` that is private to this file emits an event with the specified `id`. - constructor: (rootPath, regex, options) -> + constructor: (directory, regex, options) -> scanHandlerOptions = ignoreCase: regex.ignoreCase inclusions: options.inclusions @@ -17,10 +15,10 @@ class DirectorySearch @task = new Task(require.resolve('./scan-handler')) @task.on 'scan:result-found', options.didMatch @task.on 'scan:file-error', options.didError - @task.on 'scan:paths-searched', options.didSearchPaths + @task.on 'scan:paths-searched', (count) -> options.didSearchPaths(directory, count) @promise = new Promise (resolve, reject) => @task.on('task:cancelled', reject) - @task.start([rootPath], regex.source, scanHandlerOptions, resolve) + @task.start([directory.getPath()], regex.source, scanHandlerOptions, resolve) # Public: Implementation of `then()` to satisfy the *thenable* contract. # This makes it possible to use a `DirectorySearch` with `Promise.all()`. @@ -66,6 +64,7 @@ class DefaultDirectorySearcher # * `range` {Range} Identifies the matching region in the file. (Likely as an array of numeric arrays.) # * `didError` {Function} call with an Error if there is a problem during the search. # * `didSearchPaths` {Function} periodically call with the number of paths searched thus far. + # This takes two arguments: the `Directory` and the count. # * `inclusions` {Array} of glob patterns (as strings) to search within. Note that this # array may be empty, indicating that all files should be searched. # @@ -80,15 +79,16 @@ class DefaultDirectorySearcher # Returns a *thenable* `DirectorySearch` that includes a `cancel()` method. If `cancel()` is # invoked before the `DirectorySearch` is determined, it will reject the `DirectorySearch`. search: (directories, regex, options) -> - rootPaths = directories.map (directory) -> directory.getPath() + # Make a mutable copy of the directories array. + directories = directories.slice(0) isCancelled = false promise = new Promise (resolve, reject) -> run = -> if isCancelled reject() - else if rootPaths.length - rootPath = rootPaths.shift() - thenable = new DirectorySearch(rootPath, regex, options) + else if directories.length + directory = directories.shift() + thenable = new DirectorySearch(directory, regex, options) thenable.then(run, reject) else resolve() diff --git a/src/workspace.coffee b/src/workspace.coffee index a2d3059de..638fa8a0a 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -811,15 +811,20 @@ class Workspace extends Model iterator = options options = {} - # Find a searcher for every Directory in the project. - searchersAndDirectories = [] + # Find a searcher for every Directory in the project. Each searcher that is matched + # will be associated with an Array of Directory objects in the Map. + directoriesForSearcher = new Map() for directory in atom.project.getDirectories() searcher = @defaultDirectorySearcher for directorySearcher in @directorySearchers if directorySearcher.canSearchDirectory(directory) searcher = directorySearcher break - searchersAndDirectories.push({searcher, directory}) + directories = directoriesForSearcher.get(searcher) + unless directories + directories = [] + directoriesForSearcher.set(searcher, directories) + directories.push(directory) # Define the onPathsSearched callback. if _.isFunction(options.onPathsSearched) @@ -838,22 +843,23 @@ class Workspace extends Model else onPathsSearched = -> + # Build up the options object that will be shared by all searchers. + searchOptions = + inclusions: options.paths or [] + includeHidden: true + excludeVcsIgnores: atom.config.get('core.excludeVcsIgnoredPaths') + exclusions: atom.config.get('core.ignoredNames') + follow: atom.config.get('core.followSymlinks') + didMatch: (result) -> + iterator(result) unless atom.project.isPathModified(result.filePath) + didError: (error) -> + iterator(null, error) + didSearchPaths: onPathsSearched + # Kick off all of the searches and unify them into one Promise. allSearches = [] - for entry in searchersAndDirectories - {searcher, directory} = entry - searchOptions = - inclusions: options.paths or [] - includeHidden: true - excludeVcsIgnores: atom.config.get('core.excludeVcsIgnoredPaths') - exclusions: atom.config.get('core.ignoredNames') - follow: atom.config.get('core.followSymlinks') - didMatch: (result) -> - iterator(result) unless atom.project.isPathModified(result.filePath) - didError: (error) -> - iterator(null, error) - didSearchPaths: onPathsSearched.bind(undefined, directory) - directorySearcher = searcher.search([directory], regex, searchOptions) + directoriesForSearcher.forEach (directories, searcher) -> + directorySearcher = searcher.search(directories, regex, searchOptions) allSearches.push(directorySearcher) searchPromise = Promise.all(allSearches)