Merge pull request #15377 from atom/aw-async-watchpath

Async watchPath method
This commit is contained in:
Ash Wilson
2017-08-18 15:51:44 -04:00
committed by GitHub
6 changed files with 84 additions and 59 deletions

View File

@@ -431,6 +431,14 @@ class NativeWatcherRegistry {
watcher.attachToNative(native, nativePath)
})
}
// Private: Generate a visual representation of the currently active watchers managed by this
// registry.
//
// Returns a {String} showing the tree structure.
print () {
return this.tree.print()
}
}
module.exports = {NativeWatcherRegistry}

View File

@@ -339,7 +339,7 @@ class NativeWatcher {
// ```js
// const {watchPath} = require('atom')
//
// const disposable = watchPath('/var/log', {}, events => {
// const disposable = await watchPath('/var/log', {}, events => {
// console.log(`Received batch of ${events.length} events.`)
// for (const event of events) {
// // "created", "modified", "deleted", "renamed"
@@ -424,6 +424,8 @@ class PathWatcher {
// intend to assert about because there will be a delay between the instantiation of the watcher and the activation
// of the underlying OS resources that feed it events.
//
// PathWatchers acquired through `watchPath` are already started.
//
// ```js
// const {watchPath} = require('atom')
// const ROOT = path.join(__dirname, 'fixtures')
@@ -505,14 +507,16 @@ class PathWatcher {
}))
this.subs.add(native.onShouldDetach(({replacement, watchedPath}) => {
if (replacement !== native && this.normalizedPath.startsWith(watchedPath)) {
if (this.native === native && replacement !== native && this.normalizedPath.startsWith(watchedPath)) {
this.attachToNative(replacement)
}
}))
this.subs.add(native.onWillStop(() => {
this.subs.dispose()
this.native = null
if (this.native === native) {
this.subs.dispose()
this.native = null
}
}))
this.resolveAttachedPromise()
@@ -579,6 +583,11 @@ class PathWatcherManager {
return watcher
}
// Private: Return a {String} depicting the currently active native watchers.
print () {
return this.nativeRegistry.print()
}
// Private: Stop all living watchers.
//
// Returns a {Promise} that resolves when all native watcher resources are disposed.
@@ -604,13 +613,13 @@ class PathWatcherManager {
// * `path` {String} containing the absolute path to the filesystem entry that was acted upon.
// * `oldPath` For rename events, {String} containing the filesystem entry's former absolute path.
//
// Returns a {PathWatcher}. Note that every {PathWatcher} is a {Disposable}, so they can be managed by
// [CompositeDisposables]{CompositeDisposable} if desired.
// Returns a {Promise} that will resolve to a {PathWatcher} once it has started. Note that every {PathWatcher}
// is a {Disposable}, so they can be managed by a {CompositeDisposable} if desired.
//
// ```js
// const {watchPath} = require('atom')
//
// const disposable = watchPath('/var/log', {}, events => {
// const disposable = await watchPath('/var/log', {}, events => {
// console.log(`Received batch of ${events.length} events.`)
// for (const event of events) {
// // "created", "modified", "deleted", "renamed"
@@ -629,7 +638,8 @@ class PathWatcherManager {
// ```
//
function watchPath (rootPath, options, eventCallback) {
return PathWatcherManager.instance().createWatcher(rootPath, options, eventCallback)
const watcher = PathWatcherManager.instance().createWatcher(rootPath, options, eventCallback)
return watcher.getStartPromise().then(() => watcher)
}
// Private: Return a Promise that resolves when all {NativeWatcher} instances associated with a FileSystemManager
@@ -638,4 +648,9 @@ function stopAllWatchers () {
return PathWatcherManager.instance().stopAllWatchers()
}
module.exports = {watchPath, stopAllWatchers}
// Private: Show the currently active native watchers.
function printWatchers () {
return PathWatcherManager.instance().print()
}
module.exports = {watchPath, stopAllWatchers, printWatchers}

View File

@@ -29,13 +29,13 @@ class Project extends Model
@repositoryPromisesByPath = new Map()
@repositoryProviders = [new GitRepositoryProvider(this, config)]
@loadPromisesByPath = {}
@watchersByPath = {}
@watcherPromisesByPath = {}
@consumeServices(packageManager)
destroyed: ->
buffer.destroy() for buffer in @buffers.slice()
repository?.destroy() for repository in @repositories.slice()
watcher.dispose() for _, watcher in @watchersByPath
watcher.dispose() for _, watcher in @watcherPromisesByPath
@rootDirectories = []
@repositories = []
@@ -140,6 +140,10 @@ class Project extends Model
#
# To watch paths outside of open projects, use the `watchPaths` function instead; see {PathWatcher}.
#
# When writing tests against functionality that uses this method, be sure to wait for the
# {Promise} returned by {getWatcherPromise()} before manipulating the filesystem to ensure that
# the watcher is receiving events.
#
# * `callback` {Function} to be called with batches of filesystem events reported by
# the operating system.
# * `events` An {Array} of objects that describe a batch of filesystem events.
@@ -212,8 +216,8 @@ class Project extends Model
@rootDirectories = []
@repositories = []
watcher.dispose() for _, watcher in @watchersByPath
@watchersByPath = {}
watcher.then((w) -> w.dispose()) for _, watcher in @watcherPromisesByPath
@watcherPromisesByPath = {}
@addPath(projectPath, emitEvent: false) for projectPath in projectPaths
@@ -229,11 +233,15 @@ class Project extends Model
return if existingDirectory.getPath() is directory.getPath()
@rootDirectories.push(directory)
@watchersByPath[directory.getPath()] = watchPath directory.getPath(), {}, (events) =>
@emitter.emit 'did-change-files', events
@watcherPromisesByPath[directory.getPath()] = watchPath directory.getPath(), {}, (events) =>
# Stop event delivery immediately on removal of a rootDirectory, even if its watcher
# promise has yet to resolve at the time of removal
if @rootDirectories.includes directory
@emitter.emit 'did-change-files', events
for root, watcher in @watchersByPath
watcher.dispose() unless @rootDirectoryies.includes root
for root, watcherPromise in @watcherPromisesByPath
unless @rootDirectories.includes root
watcherPromise.then (watcher) -> watcher.dispose()
repo = null
for provider in @repositoryProviders
@@ -250,6 +258,21 @@ class Project extends Model
directory ?= @defaultDirectoryProvider.directoryForURISync(projectPath)
directory
# Extended: Access a {Promise} that resolves when the filesystem watcher associated with a project
# root directory is ready to begin receiving events.
#
# This is especially useful in test cases, where it's important to know that the watcher is
# ready before manipulating the filesystem to produce events.
#
# * `projectPath` {String} One of the project's root directories.
#
# Returns a {Promise} that resolves with the {PathWatcher} associated with this project root
# once it has initialized and is ready to start sending events. The Promise will reject with
# an error instead if `projectPath` is not currently a root directory.
getWatcherPromise: (projectPath) ->
@watcherPromisesByPath[projectPath] or
Promise.reject(new Error("#{projectPath} is not a project root"))
# Public: remove a path from the project's list of root paths.
#
# * `projectPath` {String} The path to remove.
@@ -268,7 +291,8 @@ class Project extends Model
[removedDirectory] = @rootDirectories.splice(indexToRemove, 1)
[removedRepository] = @repositories.splice(indexToRemove, 1)
removedRepository?.destroy() unless removedRepository in @repositories
@watchersByPath[projectPath]?.dispose()
@watcherPromisesByPath[projectPath]?.then (w) -> w.dispose()
delete @watcherPromisesByPath[projectPath]
@emitter.emit "did-change-paths", @getPaths()
true
else