Change DirectorySearcher to return a DirectorySearch.

This commit is contained in:
Michael Bolin
2015-06-02 14:04:59 -04:00
parent 986640f670
commit 7294e0cda4
3 changed files with 100 additions and 40 deletions

View File

@@ -1,5 +1,6 @@
path = require 'path'
temp = require 'temp'
{Disposable} = require 'event-kit'
Workspace = require '../src/workspace'
Pane = require '../src/pane'
{View} = require '../src/space-pen-extensions'
@@ -943,9 +944,13 @@ describe "Workspace", ->
foreignFilePath = 'ssh://foreign-directory:8080/hello.txt'
numPathsSearchedInDir2 = 1
numPathsToPretendToSearchInCustomDirectorySearcher = 10
class CustomDirectorySearcher
canSearchDirectory: (directory) -> directory.getPath() is dir1
search: (directory, regexSource, onSearchResult, onSearchError, onPathsSearched, options) ->
class CustomDirectorySearch
constructor: () ->
@promise = Promise.resolve()
then: (args...) ->
@promise.then.apply(@promise, args)
onDidMatch: (callback) ->
# Invoke the callback with the only result we plan to return.
searchResult1 =
filePath: foreignFilePath,
matches: [
@@ -956,11 +961,20 @@ describe "Workspace", ->
range: [[0, 0], [0, 5]],
}
]
onSearchResult(searchResult1)
onPathsSearched(numPathsToPretendToSearchInCustomDirectorySearcher)
promise = Promise.resolve()
promise.cancel = ->
promise
callback(searchResult1)
new Disposable
onDidError: (callback) ->
new Disposable
onDidSearchPaths: (callback) ->
# Invoke the callback with the one notification we plan to send.
callback(numPathsToPretendToSearchInCustomDirectorySearcher)
new Disposable
cancel: ->
class CustomDirectorySearcher
canSearchDirectory: (directory) -> directory.getPath() is dir1
search: (directory, options) ->
new CustomDirectorySearch
atom.packages.serviceHub.provide(
"atom.directory-searcher", "0.1.0", new CustomDirectorySearcher())
@@ -982,16 +996,27 @@ describe "Workspace", ->
it "can be cancelled by cancelling one of the DirectorySearchers", ->
customDirectorySearcherPromiseInstance = null
class CustomDirectorySearchToCancel
constructor: () ->
# Note that hoisting reject in this way is generally frowned upon.
@promise = new Promise (resolve, reject) =>
@hoistedReject = reject
customDirectorySearcherPromiseInstance = this
then: (args...) ->
@promise.then.apply(@promise, args)
onDidMatch: (callback) ->
new Disposable
onDidError: (callback) ->
new Disposable
onDidSearchPaths: (callback) ->
new Disposable
cancel: ->
@hoistedReject()
class CustomDirectorySearcherToCancel
canSearchDirectory: (directory) -> directory.getPath() is dir1
search: (directory, regexSource, onSearchResult, onSearchError, onPathsSearched, options) ->
# Note that hoisting reject in this way is generally frowned upon.
hoistedReject = null
promise = new Promise (resolve, reject) ->
hoistedReject = reject
promise.cancel = -> hoistedReject()
customDirectorySearcherPromiseInstance = promise
promise
search: (directory, options) ->
new CustomDirectorySearchToCancel
atom.packages.serviceHub.provide(
"atom.directory-searcher", "0.1.0", new CustomDirectorySearcherToCancel())

View File

@@ -1,5 +1,47 @@
Task = require './task'
# Public:
# Implements thenable so it can be used with `Promise.all()`.
class DirectorySearch
# Public:
constructor: (directory, options) ->
@task = new Task(require.resolve('./scan-handler'))
rootPaths = [directory.getPath()]
@promise = new Promise (resolve, reject) =>
myResolve = (arg) ->
resolve(arg)
@task.start(rootPaths, options.regexSource, options, myResolve)
@task.on('task:cancelled', reject)
# Public:
# Returns `Promise`.
then: (args...) ->
@promise.then.apply(@promise, args)
# Public:
# Returns `Disposable`.
onDidMatch: (callback) ->
@task.on 'scan:result-found', callback
# Public:
# Returns `Disposable`.
onDidError: (callback) ->
@task.on 'scan:file-error', callback
# Public:
#
# * `callback` {Function} called with the number of paths searched thus far.
#
# Returns `Disposable`.
onDidSearchPaths: (callback) ->
@task.on 'scan:paths-searched', callback
# Public:
cancel: ->
# This will cause @promise to reject.
@task.cancel()
# Default provider for the `atom.directory-searcher` service.
module.exports =
class DefaultDirectorySearcher
@@ -17,7 +59,6 @@ class DefaultDirectorySearcher
#
# * `directory` {Directory} that has been accepted by this provider's `canSearchDirectory()`
# predicate.
# * `regexSource` {String} regex to search with. Produced via `RegExp::source`.
# (Note this reflects the "Use Regex" option exposed via the ProjectFindView UI.)
# * `onSearchResult` {Function} Should be called with each matching search result.
# * `searchResult` {Object} with the following keys:
@@ -31,6 +72,7 @@ class DefaultDirectorySearcher
# * `onPathsSearched` {Function} callback that should be invoked periodically with the number of
# paths searched.
# * `options` {Object} with the following properties:
# * `regexSource` {String} regex to search with. Produced via `RegExp::source`.
# * `ignoreCase` {boolean}
# * `inclusions` {Array} of glob patterns (as strings) to search within. Note that this
# array may be empty, indicating that all files should be searched.
@@ -43,19 +85,7 @@ class DefaultDirectorySearcher
# * `exclusions` {Array} similar to inclusions
# * `follow` {boolean} whether symlinks should be followed
#
# Returns a `Promise` that includes a `cancel()` method. If invoked before the `Proimse` is
# Returns a `DirectorySearch` that includes a `cancel()` method. If invoked before the `Proimse` is
# determined, it will reject the `Promise`.
search: (directory, regexSource, onSearchResult, onSearchError, onPathsSearched, options) ->
task = null
rootPaths = [directory.getPath()]
promise = new Promise (resolve, reject) ->
task = Task.once require.resolve('./scan-handler'), rootPaths, regexSource, options, resolve
task.on 'task:cancelled', reject
promise.cancel = ->
task.cancel()
task.on 'scan:result-found', onSearchResult
task.on 'scan:file-error', onSearchError
task.on 'scan:paths-searched', onPathsSearched
promise
search: (directory, options) ->
new DirectorySearch(directory, options)

View File

@@ -803,13 +803,14 @@ class Workspace extends Model
# * `onPathsSearched` (optional) {Function}
# * `iterator` {Function} callback on each file found
#
# Returns a `Promise` with a `cancel()` method.
# Returns a `Promise`.
scan: (regex, options={}, iterator) ->
if _.isFunction(options)
iterator = options
options = {}
searchOptions =
regexSource: regex.source
ignoreCase: regex.ignoreCase
inclusions: options.paths or []
includeHidden: true
@@ -855,18 +856,22 @@ class Workspace extends Model
# Kick off all of the searches and unify them into one Promise.
allSearchPromises = []
disposables = new CompositeDisposable
for entry in searchersAndDirectories
{searcher, directory} = entry
directorySearcher = searcher.search(directory, searchOptions)
disposables.add(directorySearcher.onDidMatch(onSearchResult))
disposables.add(directorySearcher.onDidError(onSearchError))
recordNumberOfPathsSearched = onPathsSearched.bind(undefined, directory)
allSearchPromises.push(searcher.search(
directory,
regex.source,
onSearchResult,
onSearchError,
recordNumberOfPathsSearched,
searchOptions))
disposables.add(directorySearcher.onDidSearchPaths(recordNumberOfPathsSearched))
allSearchPromises.push(directorySearcher)
searchPromise = Promise.all(allSearchPromises)
# Make sure to clean up the disposables once the searchPromise is determined.
disposeAll = (args...) ->
disposables.dispose()
searchPromise.then(disposeAll, disposeAll)
for buffer in atom.project.getBuffers() when buffer.isModified()
filePath = buffer.getPath()
continue unless atom.project.contains(filePath)