Skip to content
This repository was archived by the owner on Dec 15, 2022. It is now read-only.

[WIP] Use atom.watchPath instead of PathWatcher #1232

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 3 additions & 38 deletions lib/directory.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ const path = require('path')
const _ = require('underscore-plus')
const {CompositeDisposable, Emitter} = require('atom')
const fs = require('fs-plus')
const PathWatcher = require('pathwatcher')
const File = require('./file')
const {repoForPath} = require('./helpers')

Expand Down Expand Up @@ -79,7 +78,6 @@ class Directory {

destroy () {
this.destroyed = true
this.unwatch()
this.subscriptions.dispose()
this.emitter.emit('did-destroy')
}
Expand Down Expand Up @@ -239,36 +237,6 @@ class Directory {
return false
}

// Public: Stop watching this directory for changes.
unwatch () {
if (this.watchSubscription != null) {
this.watchSubscription.close()
this.watchSubscription = null
}

for (let [key, entry] of this.entries) {
entry.destroy()
this.entries.delete(key)
}
}

// Public: Watch this directory for changes.
watch () {
if (this.watchSubscription != null) return
try {
this.watchSubscription = PathWatcher.watch(this.path, eventType => {
switch (eventType) {
case 'change':
this.reload()
break
case 'delete':
this.destroy()
break
}
})
} catch (error) {}
}

getEntries () {
let names
try {
Expand Down Expand Up @@ -396,20 +364,17 @@ class Directory {
}
}

// Public: Collapse this directory and stop watching it.
// Public: Collapse this directory
collapse () {
this.expansionState.isExpanded = false
this.expansionState = this.serializeExpansionState()
this.unwatch()
this.emitter.emit('did-collapse')
}

// Public: Expand this directory, load its children, and start watching it for
// changes.
// Public: Expand this directory
expand () {
this.expansionState.isExpanded = true
this.reload()
this.watch()
// this.reload()
this.emitter.emit('did-expand')
}

Expand Down
202 changes: 202 additions & 0 deletions lib/root-directory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
const {CompositeDisposable, watchPath} = require('atom')

const _ = require('underscore-plus')
const fs = require('fs-plus')
const path = require('path')

const File = require('./file')
const Directory = require('./directory')

module.exports =
class RootDirectory extends Directory {
constructor ({name, fullPath, symlink, expansionState, isRoot, ignoredNames, useSyncFS, stats}) {
super({name, fullPath, symlink, expansionState, isRoot, ignoredNames, useSyncFS, stats})

this.directories = new Map()
this.files = new Map()

this.disposables = new CompositeDisposable()
this.disposables.add(this.onDidDeletePath(deletedPath => {
// todo
}))
this.disposables.add(this.onDidCreatePath(addedPath => {
// todo
}))
this.disposables.add(this.onDidRenamePath((newPath, oldPath) => {
// todo
}))

this.loadEntries()
this.watch()
}

async destroy () {
super.destroy()
await this.unwatch()
this.disposables.dispose()
}

onDidDeletePath (callback) {
return this.emitter.on('did-delete-path', callback)
}

onDidRenamePath (callback) {
return this.emitter.on('did-rename-path', callback)
}

onDidCreatePath (callback) {
return this.emitter.on('did-create-path', callback)
}

onDidModifyPath (callback) {
return this.emitter.on('did-modify-path', callback)
}

loadEntries () {
fs.readdir(this.path, (err, names) => {
if (err) {
names = []
atom.notifications.addWarning(`Could not read files in ${this.path}`, err.message)
}

names.sort(new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'}).compare)

for (let name of names) {
const fullPath = path.join(this.path, name)
if (this.isPathIgnored(fullPath)) continue

fs.lstat(fullPath, (err, stats) => {
if (err) return

const symlink = stats.isSymbolicLink()
if (symlink) {
// TODO
// stats = fs.statSyncNoException(fullPath)
}

const statsFlat = _.pick(stats, _.keys(stats))
for (let key of ['atime', 'birthtime', 'ctime', 'mtime']) {
statsFlat[key] = statsFlat[key] && statsFlat[key].getTime()
}

if (stats.isDirectory()) {
const expansionState = this.expansionState.entries.get(name)
const directory = new Directory({
name,
fullPath,
symlink,
expansionState,
ignoredNames: this.ignoredNames,
useSyncFS: this.useSyncFS,
stats: statsFlat
})
this.directories.set(fullPath, directory)
} else if (stats.isFile()) {
const file = new File({name, fullPath, symlink, ignoredNames: this.ignoredNames, useSyncFS: this.useSyncFS, stats: statsFlat})
this.files.set(fullPath, file)
}
})
}

// return this.sortEntries(directories.concat(files))
})
}

// Public: Watch this project for changes.
async watch () {
if (this.watchSubscription != null) return
try {
this.watchSubscription = await watchPath(this.path, {}, events => {
// let reload = false
for (const event of events) {
console.log(event)
if (this.isPathIgnored(event.path)) continue
const relativePath = path.relative(this.path, event.path)
if (event.action === 'deleted') {
if (event.kind === 'file') {
this.files.get(relativePath).destroy()
} else if (event.kind === 'directory') {
this.directories.get(relativePath).destroy()
}
this.emitter.emit('did-delete-path', event.path)
} else if (event.action === 'renamed') {
// TODO: Will this be emitted if we move the file out of the root?
if (event.kind === 'file') {
this.files.set(relativePath, this.files.get(path.relative(this.path, event.oldPath)))
} else if (event.kind === 'directory') {
this.directories.set(relativePath, this.files.get(path.relative(this.path, event.oldPath)))
}
this.emitter.emit('did-rename-path', event.path, event.oldPath)
} else if (event.action === 'created') {
if (event.kind === 'file') {
fs.lstat(event.path, (err, stats) => {
if (err) return

const symlink = stats.isSymbolicLink()
if (symlink) {
// TODO
// stats = fs.statSyncNoException(fullPath)
}

const statsFlat = _.pick(stats, _.keys(stats))
for (let key of ['atime', 'birthtime', 'ctime', 'mtime']) {
statsFlat[key] = statsFlat[key] && statsFlat[key].getTime()
}

const file = new File({name, fullPath: event.path, symlink, ignoredNames: this.ignoredNames, useSyncFS: this.useSyncFS, stats: statsFlat})
this.files.set(relativePath, file)
})
} else if (event.kind === 'directory') {
fs.lstat(event.path, (err, stats) => {
if (err) return

const symlink = stats.isSymbolicLink()
if (symlink) {
// TODO
// stats = fs.statSyncNoException(event.path)
}

const statsFlat = _.pick(stats, _.keys(stats))
for (let key of ['atime', 'birthtime', 'ctime', 'mtime']) {
statsFlat[key] = statsFlat[key] && statsFlat[key].getTime()
}

const expansionState = this.expansionState.entries.get(name)
const directory = new Directory({
name,
fullPath: event.path,
symlink,
expansionState,
ignoredNames: this.ignoredNames,
useSyncFS: this.useSyncFS,
stats: statsFlat
})
this.directories.set(relativePath, directory)
})
}
this.emitter.emit('did-create-path', event.path)
} else if (event.action === 'modified') {
this.emitter.emit('did-modify-path', event.path)
}
}
})
} catch (error) {} // TODO

this.reload()
}

// Public: Stop watching this project for changes.
async unwatch () {
if (this.watchSubscription != null) {
await this.watchSubscription
console.log(this.watchSubscription)
this.watchSubscription.dispose()
this.watchSubscription = null
}

for (let [key, entry] of this.entries) {
entry.destroy()
this.entries.delete(key)
}
}
}
3 changes: 2 additions & 1 deletion lib/tree-view.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ CopyDialog = require './copy-dialog'
IgnoredNames = null # Defer requiring until actually needed

Directory = require './directory'
RootDirectory = require './root-directory'
DirectoryView = require './directory-view'
RootDragAndDrop = require './root-drag-and-drop'

Expand Down Expand Up @@ -332,7 +333,7 @@ class TreeView
for key in ["atime", "birthtime", "ctime", "mtime"]
stats[key] = stats[key].getTime()

directory = new Directory({
directory = new RootDirectory({
name: path.basename(projectPath)
fullPath: projectPath
symlink: false
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
"dependencies": {
"fs-plus": "^3.0.0",
"minimatch": "~0.3.0",
"pathwatcher": "^8.0.0",
"@atom/temp": "~0.8.4",
"underscore-plus": "^1.0.0"
},
Expand Down
31 changes: 23 additions & 8 deletions spec/tree-view-package-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@ describe "TreeView", ->
root2 = treeView.roots[1]
sampleJs = files[0]
sampleTxt = files[1]
expect(root1.directory.watchSubscription).toBeTruthy()

waitsFor ->
root1.directory.watchSubscription

runs ->
expect(root1.directory.watchSubscription).toBeTruthy()

afterEach ->
if treeViewOpenPromise = atom.packages.getActivePackage('tree-view')?.mainModule.treeViewOpenPromise
Expand Down Expand Up @@ -551,15 +556,25 @@ describe "TreeView", ->
grandchild = child.querySelector('li')
grandchild.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))

expect(root1.directory.watchSubscription).toBeTruthy()
expect(child.directory.watchSubscription).toBeTruthy()
expect(grandchild.directory.watchSubscription).toBeTruthy()
waitsFor ->
root1.directory.watchSubscription

root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))
waitsFor ->
child.directory.watchSubscription

waitsFor ->
grandchild.directory.watchSubscription

runs ->
expect(root1.directory.watchSubscription).toBeTruthy()
expect(child.directory.watchSubscription).toBeTruthy()
expect(grandchild.directory.watchSubscription).toBeTruthy()

root1.dispatchEvent(new MouseEvent('click', {bubbles: true, detail: 1}))

expect(root1.directory.watchSubscription).toBeFalsy()
expect(child.directory.watchSubscription).toBeFalsy()
expect(grandchild.directory.watchSubscription).toBeFalsy()
expect(root1.directory.watchSubscription).toBeFalsy()
expect(child.directory.watchSubscription).toBeFalsy()
expect(grandchild.directory.watchSubscription).toBeFalsy()

describe "when mouse down fires on a file or directory", ->
it "selects the entry", ->
Expand Down