diff --git a/.vscode/launch.json b/.vscode/launch.json index aec9e62f8bec6..a1d23fdac8795 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,6 +9,9 @@ "request": "launch", "name": "Launch Backend", "program": "${workspaceRoot}/examples/browser/lib/backend/main.js", + "args": [ + "--loglevel=debug" + ], "sourceMaps": true, "outFiles": [ "${workspaceRoot}/examples/browser/lib/**/*.js", @@ -21,6 +24,7 @@ "name": "Launch Backend (eclipse.jdt.ls)", "program": "${workspaceRoot}/examples/browser/lib/backend/main.js", "args": [ + "--loglevel=debug", "--root-dir=${workspaceRoot}/../eclipse.jdt.ls/org.eclipse.jdt.ls.core" ], "sourceMaps": true, diff --git a/src/filesystem/browser/file-tree/file-tree-model.ts b/src/filesystem/browser/file-tree/file-tree-model.ts index 9a954c41ccae9..ff3d9fbf9e023 100644 --- a/src/filesystem/browser/file-tree/file-tree-model.ts +++ b/src/filesystem/browser/file-tree/file-tree-model.ts @@ -8,7 +8,7 @@ import { injectable, inject } from "inversify"; import URI from '../../../application/common/uri'; import { ICompositeTreeNode, TreeModel, TreeServices } from "../../../application/browser"; -import { FileSystem, FileSystemWatcher, FileChangesEvent, FileChangeType } from "../../../filesystem/common"; +import { FileSystem, FileSystemWatcher, FileChangeType, FileChange } from "../../../filesystem/common"; import { FileStatNode, DirNode, FileTree } from "./file-tree"; import { LocationService } from '../location'; @@ -29,7 +29,7 @@ export class FileTreeModel extends TreeModel implements LocationService { @inject(FileTreeServices) services: FileTreeServices ) { super(tree, services); - this.toDispose.push(this.watcher.onFileChanges(event => this.onFileChanges(event))); + this.toDispose.push(this.watcher.onFilesChanged(changes => this.onFilesChanged(changes))); } get location(): URI | undefined { @@ -59,30 +59,30 @@ export class FileTreeModel extends TreeModel implements LocationService { return undefined; } - protected onFileChanges(event: FileChangesEvent): void { - const affectedNodes = this.getAffectedNodes(event); + protected onFilesChanged(changes: FileChange[]): void { + const affectedNodes = this.getAffectedNodes(changes); if (affectedNodes.length !== 0) { affectedNodes.forEach(node => this.refresh(node)); - } else if (this.isRootAffected(event)) { + } else if (this.isRootAffected(changes)) { this.refresh(); } } - protected isRootAffected(event: FileChangesEvent): boolean { + protected isRootAffected(changes: FileChange[]): boolean { const root = this.root; if (FileStatNode.is(root)) { - return event.changes.some(change => - change.type < FileChangeType.DELETED && change.uri === root.fileStat.uri + return changes.some(change => + change.type < FileChangeType.DELETED && change.uri.toString() === root.uri.toString() ); } return false; } - protected getAffectedNodes(event: FileChangesEvent): ICompositeTreeNode[] { + protected getAffectedNodes(changes: FileChange[]): ICompositeTreeNode[] { const nodes: DirNode[] = []; - for (const change of event.changes) { + for (const change of changes) { const uri = change.uri; - const id = change.type > FileChangeType.UPDATED ? new URI(uri).parent.toString() : uri; + const id = change.type > FileChangeType.UPDATED ? uri.parent.toString() : uri.toString(); const node = this.getNode(id); if (DirNode.is(node) && node.expanded) { nodes.push(node); diff --git a/src/filesystem/browser/filesystem-client-module.ts b/src/filesystem/browser/filesystem-client-module.ts index 8b48d0a9e8955..4f4995a0312f5 100644 --- a/src/filesystem/browser/filesystem-client-module.ts +++ b/src/filesystem/browser/filesystem-client-module.ts @@ -8,21 +8,28 @@ import { ContainerModule } from 'inversify'; import { CommandContribution, MenuContribution, ResourceResolver } from '../../application/common'; import { WebSocketConnectionProvider } from '../../messaging/browser/connection'; -import { FileSystem, FileSystemWatcher, FileResourceResolver } from "../common"; +import { FileSystem, FileSystemWatcher, FileResourceResolver, fileSystemPath, FileSystemWatcherClientListener } from "../common"; +import { FileSystemWatcherServer, fileSystemWatcherPath } from "../common/filesystem-watcher-protocol"; import { FileCommandContribution, FileMenuContribution } from './filesystem-commands'; export const fileSystemClientModule = new ContainerModule(bind => { + bind(FileSystemWatcherClientListener).toSelf().inSingletonScope(); + bind(FileSystemWatcherServer).toDynamicValue(ctx => { + const connection = ctx.container.get(WebSocketConnectionProvider); + const target = ctx.container.get(FileSystemWatcherClientListener); + return connection.createProxy(fileSystemWatcherPath, target); + }).inSingletonScope(); bind(FileSystemWatcher).toSelf().inSingletonScope(); + bind(FileSystem).toDynamicValue(ctx => { const connection = ctx.container.get(WebSocketConnectionProvider); - const fileSystemClient = ctx.container.get(FileSystemWatcher).getFileSystemClient(); - return connection.createProxy("/filesystem", fileSystemClient); + return connection.createProxy(fileSystemPath); }).inSingletonScope(); bind(FileResourceResolver).toSelf().inSingletonScope(); bind(ResourceResolver).toDynamicValue(ctx => ctx.container.get(FileResourceResolver)); - bind(CommandContribution).to(FileCommandContribution).inSingletonScope(); - bind(MenuContribution).to(FileMenuContribution).inSingletonScope(); + bind(CommandContribution).to(FileCommandContribution).inSingletonScope(); + bind(MenuContribution).to(FileMenuContribution).inSingletonScope(); }); diff --git a/src/filesystem/common/filesystem-watcher-protocol.ts b/src/filesystem/common/filesystem-watcher-protocol.ts new file mode 100644 index 0000000000000..015cb0e684e11 --- /dev/null +++ b/src/filesystem/common/filesystem-watcher-protocol.ts @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 TypeFox and others. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + */ + +import { Disposable } from '../../application/common'; + +export const fileSystemWatcherPath = '/fs-watcher'; + +export const FileSystemWatcherServer = Symbol('FileSystemWatcherServer'); +export interface FileSystemWatcherServer extends Disposable { + /** + * Allows to start a watcher that reports file change events on the provided resource. + * + * Resolve when watching of the given uri is started. + * Reject if a file for the given uri does not exist. + */ + watchFileChanges(uri: string): Promise; + + /** + * Allows to stop a watcher on the provided resource or absolute fs path. + */ + unwatchFileChanges(uri: string): Promise; +} + +export interface FileSystemWatcherClient extends Disposable { + /** + * Notifies about file changes + */ + onDidFilesChanged(event: DidFilesChangedParams): void; +} + +export interface DidFilesChangedParams { + changes: FileChange[]; +} + +export interface FileChange { + uri: string; + type: FileChangeType; +} + +export enum FileChangeType { + UPDATED = 0, + ADDED = 1, + DELETED = 2 +} \ No newline at end of file diff --git a/src/filesystem/common/filesystem-watcher.ts b/src/filesystem/common/filesystem-watcher.ts index 3020dab525465..8a2dee8cc476a 100644 --- a/src/filesystem/common/filesystem-watcher.ts +++ b/src/filesystem/common/filesystem-watcher.ts @@ -5,53 +5,79 @@ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 */ -import { injectable } from "inversify"; -import { Emitter, Event } from '../../application/common'; +import { injectable, inject } from "inversify"; +import { Emitter, Event, Disposable, DisposableCollection } from '../../application/common'; +import URI from "../../application/common/uri"; +import { FileChangeType, DidFilesChangedParams, FileSystemWatcherClient, FileSystemWatcherServer } from "./filesystem-watcher-protocol"; + +export { + FileChangeType +} + +export interface FileChange { + uri: URI; + type: FileChangeType; +} @injectable() -export class FileSystemWatcher { - - getFileSystemClient(): FileSystemClient { - const emitter = this.onFileChangesEmitter - return { - onFileChanges(event: FileChangesEvent) { - emitter.fire(event) - } - } +export class FileSystemWatcherClientListener implements FileSystemWatcherClient { + + protected readonly onFileChangedEmitter = new Emitter(); + + dispose(): void { + this.onFileChangedEmitter.dispose(); } - private onFileChangesEmitter = new Emitter(); + get onFilesChanged(): Event { + return this.onFileChangedEmitter.event; + } - get onFileChanges(): Event { - return this.onFileChangesEmitter.event; + onDidFilesChanged(event: DidFilesChangedParams): void { + const changes = event.changes.map(change => { + uri: new URI(change.uri), + type: change.type + }); + this.onFileChangedEmitter.fire(changes); } -} -export interface FileSystemClient { - /** - * Notifies about file changes - */ - onFileChanges(event: FileChangesEvent): void } -export class FileChangesEvent { - constructor(public readonly changes: FileChange[]) { } -} +@injectable() +export class FileSystemWatcher implements Disposable { -export class FileChange { + protected readonly toDispose = new DisposableCollection(); constructor( - public readonly uri: string, - public readonly type: FileChangeType) { } + @inject(FileSystemWatcherServer) protected readonly server: FileSystemWatcherServer, + @inject(FileSystemWatcherClientListener) protected readonly listener: FileSystemWatcherClientListener + ) { + this.toDispose.push(server); + this.toDispose.push(listener); + } - equals(other: any): boolean { - return other instanceof FileChange && other.type === this.type && other.uri === this.uri; + dispose(): void { + this.toDispose.dispose(); } -} + /** + * Allows to start a watcher that reports file change events on the provided resource. + */ + watchFileChanges(uri: URI): Promise { + const raw = uri.toString(); + return this.server.watchFileChanges(raw).then(() => { + const disposable = Disposable.create(() => + this.server.unwatchFileChanges(raw) + ); + this.toDispose.push(disposable); + return disposable; + }); + } -export enum FileChangeType { - UPDATED = 0, - ADDED = 1, - DELETED = 2 + /** + * Notifies about file changes + */ + get onFilesChanged(): Event { + return this.listener.onFilesChanged; + } } + diff --git a/src/filesystem/common/filesystem.ts b/src/filesystem/common/filesystem.ts index 1959336b1378c..3b9c9413d75d1 100644 --- a/src/filesystem/common/filesystem.ts +++ b/src/filesystem/common/filesystem.ts @@ -7,6 +7,8 @@ import { Disposable } from '../../application/common'; +export const fileSystemPath = '/filesystem'; + export const FileSystem = Symbol("FileSystem"); export interface FileSystem extends Disposable { diff --git a/src/filesystem/common/index.ts b/src/filesystem/common/index.ts index 1db131abb6ff0..d45520efc7563 100644 --- a/src/filesystem/common/index.ts +++ b/src/filesystem/common/index.ts @@ -5,7 +5,7 @@ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 */ -export * from './filesystem' -export * from './filesystem-selection' -export * from './filesystem-watcher' +export * from './filesystem'; +export * from './filesystem-selection'; +export * from './filesystem-watcher'; export * from './file-resource'; diff --git a/src/filesystem/node/filesystem-server-module.ts b/src/filesystem/node/filesystem-server-module.ts index bf0232a705d22..f24eec2c72867 100644 --- a/src/filesystem/node/filesystem-server-module.ts +++ b/src/filesystem/node/filesystem-server-module.ts @@ -1,4 +1,3 @@ - /* * Copyright (C) 2017 TypeFox and others. * @@ -9,18 +8,40 @@ import { ContainerModule } from "inversify"; import { ConnectionHandler, JsonRpcConnectionHandler } from "../../messaging/common"; import { FileSystemNode } from './node-filesystem'; -import { FileSystemWatcher } from '../common/filesystem-watcher'; -import { FileSystem } from "../common/filesystem"; +import { FileSystemWatcher, FileSystem, fileSystemPath, FileSystemWatcherClientListener } from "../common"; +import { FileSystemWatcherServer, FileSystemWatcherClient, fileSystemWatcherPath } from '../common/filesystem-watcher-protocol'; +import { NodeFileSytemWatcherServer } from './node-filesystem-watcher'; export const fileSystemServerModule = new ContainerModule(bind => { - bind(FileSystemWatcher).toSelf(); - bind(FileSystemNode).toSelf().inSingletonScope(); bind(FileSystem).toDynamicValue(ctx => ctx.container.get(FileSystemNode)).inSingletonScope(); bind(ConnectionHandler).toDynamicValue(ctx => - new JsonRpcConnectionHandler("/filesystem", () => + new JsonRpcConnectionHandler(fileSystemPath, () => ctx.container.get(FileSystem) ) ).inSingletonScope(); + + bind(NodeFileSytemWatcherServer).toSelf(); + bind(ConnectionHandler).toDynamicValue(ctx => + new JsonRpcConnectionHandler(fileSystemWatcherPath, (client, connection) => { + const server = ctx.container.get(NodeFileSytemWatcherServer); + connection.onDispose(() => server.dispose()); + server.setClient(client); + return server; + }) + ).inSingletonScope(); + + bind(FileSystemWatcher).toSelf(); + bind(FileSystemWatcherClientListener).toSelf(); + bind(FileSystemWatcher).toDynamicValue(({ container }) => { + const client = container.get(FileSystemWatcherClientListener); + const server = container.get(NodeFileSytemWatcherServer); + server.setClient(client); + + const child = container.createChild(); + child.bind(FileSystemWatcherClientListener).toConstantValue(client); + child.bind(FileSystemWatcherServer).toConstantValue(server); + return child.get(FileSystemWatcher); + }); }); diff --git a/src/filesystem/node/node-filesystem-watcher.ts b/src/filesystem/node/node-filesystem-watcher.ts new file mode 100644 index 0000000000000..e2d254c730c49 --- /dev/null +++ b/src/filesystem/node/node-filesystem-watcher.ts @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2017 TypeFox and others. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + */ + +import { ChokidarWatcher as Watcher, watch } from "chokidar"; +import { injectable, inject } from "inversify"; +import URI from "../../application/common/uri"; +import { Disposable, DisposableCollection, ILogger } from '../../application/common'; +import { FileUri } from "../../application/node"; +import { + FileChange, + FileChangeType, + FileSystemWatcherClient, + FileSystemWatcherServer +} from '../common/filesystem-watcher-protocol'; + +@injectable() +export class NodeFileSytemWatcherServer implements FileSystemWatcherServer { + + protected _watcher: Promise | undefined; + protected client: FileSystemWatcherClient | undefined; + + protected readonly toDispose = new DisposableCollection(); + + protected changes: FileChange[] = []; + protected readonly fireDidFilesChangedTimeout = 50; + protected readonly toDisposeOnFileChange = new DisposableCollection(); + + constructor( + @inject(ILogger) protected readonly logger: ILogger + ) { } + + dispose(): void { + this.toDispose.dispose(); + } + + watchFileChanges(uri: string): Promise { + const paths = this.toPaths(uri); + if (this._watcher) { + return this._watcher.then(watcher => { + watcher.add(paths); + this.logger.debug('Watch: ', paths); + }); + } + this._watcher = new Promise(resolve => { + const watcher = this.createWatcher(paths); + watcher.once('ready', () => + resolve(watcher) + ); + }); + this.toDispose.push(Disposable.create(() => { + this._watcher = undefined; + })); + return this._watcher.then(() => { }); + } + + unwatchFileChanges(uri: string): Promise { + if (this._watcher) { + return this._watcher.then(watcher => { + const paths = this.toPaths(uri); + watcher.unwatch(paths); + this.logger.debug('Unwatch: ', paths); + }); + } + return Promise.resolve(); + } + + setClient(client: FileSystemWatcherClient) { + this.client = client; + } + + protected createWatcher(paths: string | string[]): Watcher { + const watcher = watch(paths, { + ignoreInitial: true + }); + watcher.on('error', error => + this.logger.error('Watching error: ', error) + ); + watcher.on('add', path => this.pushAdded(path)); + watcher.on('addDir', path => this.pushAdded(path)); + watcher.on('change', path => this.pushUpdated(path)); + watcher.on('unlink', path => this.pushDeleted(path)); + watcher.on('unlinkDir', path => this.pushDeleted(path)); + this.toDispose.push(Disposable.create(() => { + this.logger.debug('Stop watching.'); + watcher.close(); + })); + return watcher; + } + + protected toPaths(raw: string): string | string[] { + return FileUri.fsPath(new URI(raw)); + } + + protected pushAdded(path: string): void { + this.pushFileChange(path, FileChangeType.ADDED); + } + + protected pushUpdated(path: string): void { + this.pushFileChange(path, FileChangeType.UPDATED); + } + + protected pushDeleted(path: string): void { + this.pushFileChange(path, FileChangeType.DELETED); + } + + protected pushFileChange(path: string, type: FileChangeType): void { + const uri = FileUri.create(path).toString(); + this.changes.push({ uri, type }); + + this.toDisposeOnFileChange.dispose(); + const timer = setTimeout(() => this.fireDidFilesChanged(), this.fireDidFilesChangedTimeout); + this.toDisposeOnFileChange.push(Disposable.create(() => clearTimeout(timer))); + } + + protected fireDidFilesChanged(): void { + const changes = this.changes; + this.changes = []; + const event = { changes }; + if (this.client) { + this.client.onDidFilesChanged(event); + } + this.logger.debug('Files changed: ', event); + } + +} \ No newline at end of file diff --git a/src/filesystem/node/node-filesystem.spec.ts b/src/filesystem/node/node-filesystem.spec.ts index f662a18a91215..8488f9d44751e 100644 --- a/src/filesystem/node/node-filesystem.spec.ts +++ b/src/filesystem/node/node-filesystem.spec.ts @@ -5,14 +5,18 @@ * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 */ -import * as chai from "chai"; -import * as chaiAsPromised from "chai-as-promised"; -import * as fs from "fs-extra"; -import * as os from "os"; +import * as chai from 'chai'; +import * as assert from 'assert'; +import * as chaiAsPromised from 'chai-as-promised'; +import * as fs from 'fs-extra'; +import * as os from 'os'; import URI from "../../application/common/uri"; -import { FileUri } from "../../application/node/file-uri"; +import { FileUri } from "../../application/node"; import { FileSystem } from "../common/filesystem"; +import { FileSystemWatcher, FileSystemWatcherClientListener, FileChange, FileChangeType } from '../common'; import { FileSystemNode } from "./node-filesystem"; +import { NodeFileSytemWatcherServer } from './node-filesystem-watcher'; + const expect = chai.expect; const uuidV1 = require('uuid/v1'); @@ -25,6 +29,7 @@ describe("NodeFileSystem", () => { let root: URI; let fileSystem: FileSystem; + let watcher: FileSystemWatcher; before(() => { chai.config.showDiff = true; @@ -41,9 +46,12 @@ describe("NodeFileSystem", () => { roots.push(root); fileSystem = createFileSystem(); fileSystems.push(fileSystem); + + watcher = createFileSystemWatcher(); }); after(() => { + watcher.dispose(); roots.map(root => FileUri.fsPath(root)).forEach(root => { if (fs.existsSync(root)) { try { @@ -734,50 +742,50 @@ describe("NodeFileSystem", () => { }); - /*describe("#13 watchFileChanges", () => { + describe("#13 watchFileChanges", () => { it("Should receive file changes events from in the workspace by default.", function (done) { this.timeout(4000); - let expectedEvents = [ - new FileChange(root.toString(), FileChangeType.ADDED), - new FileChange(root.appendPath("foo").toString(), FileChangeType.ADDED), - new FileChange(root.appendPath("foo").appendPath("bar").toString(), FileChangeType.ADDED), - new FileChange(root.appendPath("foo").appendPath("bar").appendPath("baz.txt").toString(), FileChangeType.ADDED) + const type = FileChangeType.ADDED; + const expectedChanges: FileChange[] = [ + { uri: root.appendPath("foo"), type }, + { uri: root.withPath(root.path.join('foo', 'bar')), type }, + { uri: root.withPath(root.path.join('foo', 'bar', 'baz.txt')), type } ]; - const fileSystem = createFileSystem(); - const client: FileSystemClient = { - onFileChanges(event: FileChangesEvent) { - const index = expectedEvents.findIndex((value, index, obj) => { - return event.changes.length === 1 && event.changes[0].equals(value); - }); - if (index >= 0) { - expectedEvents.splice(index, 1); - } - if (expectedEvents.length === 0) { - (fileSystem).setClient(undefined); - fileSystem.dispose(); - done(); - } - } - }; - (fileSystem).setClient(client); - fs.mkdirSync(FileUri.fsPath(root.appendPath("foo"))); - expect(fs.statSync(FileUri.fsPath(root.appendPath("foo"))).isDirectory()).to.be.true; - fs.mkdirSync(FileUri.fsPath(root.appendPath("foo").appendPath("bar"))); - expect(fs.statSync(FileUri.fsPath(root.appendPath("foo").appendPath("bar"))).isDirectory()).to.be.true; - fs.writeFileSync(FileUri.fsPath(root.appendPath("foo").appendPath("bar").appendPath("baz.txt")), "baz"); - expect(fs.readFileSync(FileUri.fsPath(root.appendPath("foo").appendPath("bar").appendPath("baz.txt")), "utf8")).to.be.equal("baz"); - sleep(3000).then(() => { - expect(expectedEvents).to.be.empty; + const actualChanges: FileChange[] = []; + watcher.onFilesChanged(changes => + actualChanges.push(...changes) + ); + watcher.watchFileChanges(root).then(() => { + fs.mkdirSync(FileUri.fsPath(root.appendPath("foo"))); + expect(fs.statSync(FileUri.fsPath(root.appendPath("foo"))).isDirectory()).to.be.true; + fs.mkdirSync(FileUri.fsPath(root.appendPath("foo").appendPath("bar"))); + expect(fs.statSync(FileUri.fsPath(root.appendPath("foo").appendPath("bar"))).isDirectory()).to.be.true; + fs.writeFileSync(FileUri.fsPath(root.appendPath("foo").appendPath("bar").appendPath("baz.txt")), "baz"); + expect(fs.readFileSync(FileUri.fsPath(root.appendPath("foo").appendPath("bar").appendPath("baz.txt")), "utf8")).to.be.equal("baz"); }); + setTimeout(() => { + assert.deepEqual(expectedChanges, actualChanges); + done(); + }, 2000); }); - });*/ + }); function createFileSystem(): FileSystem { return new FileSystemNode(); } + function createFileSystemWatcher(): FileSystemWatcher { + const logger: any = { + debug(): void { } + }; + const listener = new FileSystemWatcherClientListener(); + const server = new NodeFileSytemWatcherServer(logger); + server.setClient(listener); + return new FileSystemWatcher(server, listener); + } + function sleep(time: number) { return new Promise((resolve) => setTimeout(resolve, time)); } diff --git a/src/filesystem/typings/chokidar/index.d.ts b/src/filesystem/typings/chokidar/index.d.ts new file mode 100644 index 0000000000000..061a176a5db7f --- /dev/null +++ b/src/filesystem/typings/chokidar/index.d.ts @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2017 TypeFox and others. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 + */ + +import * as fs from 'fs'; +import * as chokidar from 'chokidar'; + +declare module 'chokidar' { + export type FSEvent = 'add' | 'change' | 'unlink' | 'addDir' | 'unlinkDir'; + export type WatchEvent = FSEvent | 'error' | 'ready' | 'raw'; + export class ChokidarWatcher extends chokidar.FSWatcher { + on(event: "add", listener: (path: string, stat?: fs.Stats) => void): this; + once(event: "add", listener: (path: string, stat?: fs.Stats) => void): this; + on(event: "addDir", listener: (path: string, stat?: fs.Stats) => void): this; + once(event: "addDir", listener: (path: string, stat?: fs.Stats) => void): this; + on(event: "change", listener: (path: string, stat?: fs.Stats) => void): this; + once(event: "change", listener: (path: string, stat?: fs.Stats) => void): this; + on(event: "unlink", listener: (path: string) => void): this; + once(event: "unlink", listener: (path: string) => void): this; + on(event: "unlinkDir", listener: (path: string) => void): this; + once(event: "unlinkDir", listener: (path: string) => void): this; + on(event: "error", listener: (error: Error) => void): this; + once(event: "error", listener: (error: Error) => void): this; + on(event: "ready", listener: () => void): this; + once(event: "ready", listener: () => void): this; + on(event: "raw", listener: (eventType: FSEvent, path: string, details: any) => void): this; + once(event: "raw", listener: (eventType: FSEvent, path: string, details: any) => void): this; + on(event: "all", listener: (eventType: FSEvent, path: string) => void): this; + once(event: "all", listener: (eventType: FSEvent, path: string) => void): this; + } + export function watch(paths: string | string[], options?: WatchOptions): ChokidarWatcher; +} diff --git a/src/messaging/common/proxy-factory.ts b/src/messaging/common/proxy-factory.ts index 37a7e410b1447..92344c850e3d7 100644 --- a/src/messaging/common/proxy-factory.ts +++ b/src/messaging/common/proxy-factory.ts @@ -11,13 +11,13 @@ import { ConnectionHandler } from './handler'; export class JsonRpcConnectionHandler implements ConnectionHandler { constructor( readonly path: string, - readonly targetFactory: (proxy: T) => any + readonly targetFactory: (proxy: T, connection: MessageConnection) => any ) { } onConnection(connection: MessageConnection): void { const factory = new JsonRpcProxyFactory(this.path); const proxy = factory.createProxy(); - factory.target = this.targetFactory(proxy); + factory.target = this.targetFactory(proxy, connection); factory.listen(connection); } } diff --git a/src/monaco/browser/monaco-workspace.ts b/src/monaco/browser/monaco-workspace.ts index 96b36299420b4..08aa3ba7fd30f 100644 --- a/src/monaco/browser/monaco-workspace.ts +++ b/src/monaco/browser/monaco-workspace.ts @@ -118,8 +118,8 @@ export class MonacoWorkspace extends BaseMonacoWorkspace implements lang.Workspa const disposables = new DisposableCollection() const onFileEventEmitter = new lang.Emitter() disposables.push(onFileEventEmitter); - disposables.push(this.fileSystemWatcher.onFileChanges(event => { - for (const change of event.changes) { + disposables.push(this.fileSystemWatcher.onFilesChanged(changes => { + for (const change of changes) { const result: [lang.FileChangeType, boolean | undefined] = change.type === FileChangeType.ADDED ? [lang.FileChangeType.Created, ignoreCreateEvents] : change.type === FileChangeType.UPDATED ? [lang.FileChangeType.Changed, ignoreChangeEvents] : @@ -127,7 +127,7 @@ export class MonacoWorkspace extends BaseMonacoWorkspace implements lang.Workspa const type = result[0]; const ignoreEvents = result[1]; - const uri = change.uri; + const uri = change.uri.toString(); if (ignoreEvents === undefined && ignoreEvents === false && testGlob(globPattern, uri)) { onFileEventEmitter.fire({ uri, type }); } diff --git a/src/workspace/browser/workspace-service.ts b/src/workspace/browser/workspace-service.ts index ef6020fb592db..e9a9b2b0a6294 100644 --- a/src/workspace/browser/workspace-service.ts +++ b/src/workspace/browser/workspace-service.ts @@ -1,3 +1,4 @@ +import { FileSystemWatcher } from '../../filesystem/common/filesystem-watcher'; /* * Copyright (C) 2017 TypeFox and others. * @@ -23,11 +24,15 @@ export class WorkspaceService { constructor( @inject(FileSystem) protected readonly fileSystem: FileSystem, + @inject(FileSystemWatcher) protected readonly watcher: FileSystemWatcher, @inject(WorkspaceServer) protected readonly server: WorkspaceServer ) { this.root = this.server.getRoot().then(uri => this.validateRoot(uri) ); + this.root.then(root => + watcher.watchFileChanges(new URI(root.uri)) + ); } /** diff --git a/tsconfig.json b/tsconfig.json index a7ba845aebbe0..486b0ae7e0e69 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -21,9 +21,5 @@ }, "include": [ "src" - ], - "files": [ - "src/monaco/typings/monaco/index.d.ts", - "src/filesystem/typings/drivelist/index.d.ts" ] } \ No newline at end of file