Skip to content
This repository has been archived by the owner on Apr 1, 2020. It is now read-only.

Feature/filesystem watcher #2116

Merged
merged 12 commits into from
Apr 20, 2018
5 changes: 5 additions & 0 deletions browser/src/Services/Explorer/ExplorerSplit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as path from "path"
import * as React from "react"
import { Provider } from "react-redux"
import { Store } from "redux"
import FileSystemWatcher from "./../../Services/FileSystemWatcher"

import { Event } from "oni-types"

Expand Down Expand Up @@ -58,6 +59,10 @@ export class ExplorerSplit {
rootPath: this._workspace.activeWorkspace,
})
}

FileSystemWatcher.onChange.subscribe(() => this._store.dispatch({ type: "REFRESH" }))
FileSystemWatcher.onAdd.subscribe(() => this._store.dispatch({ type: "REFRESH" }))
FileSystemWatcher.onMove.subscribe(() => this._store.dispatch({ type: "REFRESH" }))
}

public enter(): void {
Expand Down
104 changes: 104 additions & 0 deletions browser/src/Services/FileSystemWatcher/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import * as chokidar from "chokidar"
import { Stats } from "fs"
import { Event, IEvent } from "oni-types"

import * as Workspace from "./../Workspace"

export type Targets = string | string[]

interface IFSOptions {
options?: chokidar.WatchOptions
target?: Targets
}

interface IFileChangeEvent {
path: string
}

interface IStatsChangeEvent {
path: string
stats: Stats
}

export class FileSystemWatcher {
private _watcher: chokidar.FSWatcher
private _workspace: Workspace.Workspace
private _activeWorkspace: string

private _onAdd = new Event<IFileChangeEvent>()
private _onAddDir = new Event<IStatsChangeEvent>()
private _onMove = new Event<IFileChangeEvent>()
private _onChange = new Event<IFileChangeEvent>()
private _defaultOptions = { ignored: "**/node_modules" }

constructor(watch: IFSOptions = {}) {
this._workspace = Workspace.getInstance()
this._activeWorkspace = this._workspace.activeWorkspace
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering - would it make sense to hook the onDirectoryChanged event on Workspace? In that case, we'd probably want to remove the existing listeners from the old directory, and start listening to the 'new' directory.

const fileOrFolder = watch.target || this._activeWorkspace
const optionsToUse = watch.options || this._defaultOptions
this._watcher = chokidar.watch(fileOrFolder, optionsToUse)

// alternatively the ignoreInitial can be set in the config
// to avoid a flurry of events when the watcher is initialised
this._watcher.on("ready", () => {
this._attachEventListeners()
})

this._workspace.onDirectoryChanged.subscribe(newDirectory => {
this.unwatch(this._activeWorkspace)
this.watch(newDirectory)
})
}

public watch(target: Targets) {
return this._watcher.add(target)
}

public unwatch(target: Targets) {
return this._watcher.unwatch(target)
}

public close() {
this._watcher.close()
}

private _attachEventListeners() {
this._watcher.on("add", path => {
return this._onAdd.dispatch(path)
})

this._watcher.on("change", path => {
return this._onChange.dispatch(path)
})

this._watcher.on("move", path => {
return this._onMove.dispatch(path)
})

this._watcher.on("addDir", (path, stats) => {
return this._onAddDir.dispatch({ path, stats })
})
}

get allWatched(): chokidar.WatchedPaths {
return this._watcher.getWatched()
}

get onChange(): IEvent<IFileChangeEvent> {
return this._onChange
}

get onMove(): IEvent<IFileChangeEvent> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the abstraction a lot - and it seems like it'd be relatively straightforward for us to have a 'mock' implementation for tests, if we need it 👍

return this._onMove
}

get onAdd(): IEvent<IFileChangeEvent> {
return this._onAdd
}

get addDir(): IEvent<IStatsChangeEvent> {
return this._onAddDir
}
}

export default new FileSystemWatcher()
7 changes: 7 additions & 0 deletions browser/webpack.development.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,19 @@ module.exports = {
gifshot: "require('gifshot')",
"msgpack-lite": "require('msgpack-lite')",
"styled-components": "require('styled-components')",
fsevents: "require('fsevents')",
},
resolve: {
extensions: [".tsx", ".ts", ".js", ".less"],
},
module: {
rules: [
{
test: /\.(html)$/,
use: {
loader: "html-loader",
},
},
{
test: /\.less$/,
use: [
Expand Down
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -686,6 +686,7 @@
},
"license": "MIT",
"dependencies": {
"chokidar": "^2.0.3",
"dompurify": "^1.0.3",
"electron-settings": "^3.1.4",
"find-up": "2.1.0",
Expand Down Expand Up @@ -716,6 +717,7 @@
"vscode-textmate": "3.2.0"
},
"devDependencies": {
"@types/chokidar": "^1.7.5",
"@types/fs-extra": "^5.0.2",
"@types/classnames": "0.0.32",
"@types/color": "2.0.0",
Expand Down Expand Up @@ -748,6 +750,7 @@
"@types/shelljs": "^0.7.7",
"@types/sinon": "1.16.32",
"autoprefixer": "6.4.0",
"aws-sdk": "^2.202.0",
"azure-storage": "^2.8.1",
"babel-minify-webpack-plugin": "^0.3.1",
"babel-plugin-dynamic-import-node": "^1.2.0",
Expand All @@ -771,6 +774,7 @@
"find-process": "^1.1.0",
"fuse.js": "2.6.2",
"github-releases": "^0.4.1",
"html-loader": "^0.5.5",
"husky": "^0.14.3",
"innosetup-compiler": "5.5.9",
"istanbul-api": "^1.2.1",
Expand Down
Loading