Skip to content

Commit

Permalink
[fs] added JSON-RPC support for fs-watcher
Browse files Browse the repository at this point in the history
Signed-off-by: Anton Kosiakov <anton.kosyakov@typefox.io>
  • Loading branch information
akosyakov committed Jun 26, 2017
1 parent ce5057e commit c335a9d
Show file tree
Hide file tree
Showing 15 changed files with 385 additions and 104 deletions.
4 changes: 4 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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,
Expand Down
22 changes: 11 additions & 11 deletions src/filesystem/browser/file-tree/file-tree-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
17 changes: 12 additions & 5 deletions src/filesystem/browser/filesystem-client-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<FileSystemWatcherServer>(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>("/filesystem", fileSystemClient);
return connection.createProxy<FileSystem>(fileSystemPath);
}).inSingletonScope();

bind(FileResourceResolver).toSelf().inSingletonScope();
bind(ResourceResolver).toDynamicValue(ctx => ctx.container.get(FileResourceResolver));

bind<CommandContribution>(CommandContribution).to(FileCommandContribution).inSingletonScope();
bind<MenuContribution>(MenuContribution).to(FileMenuContribution).inSingletonScope();
bind(CommandContribution).to(FileCommandContribution).inSingletonScope();
bind(MenuContribution).to(FileMenuContribution).inSingletonScope();
});

48 changes: 48 additions & 0 deletions src/filesystem/common/filesystem-watcher-protocol.ts
Original file line number Diff line number Diff line change
@@ -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<void>;

/**
* Allows to stop a watcher on the provided resource or absolute fs path.
*/
unwatchFileChanges(uri: string): Promise<void>;
}

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
}
92 changes: 59 additions & 33 deletions src/filesystem/common/filesystem-watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<FileChange[]>();

dispose(): void {
this.onFileChangedEmitter.dispose();
}

private onFileChangesEmitter = new Emitter<FileChangesEvent>();
get onFilesChanged(): Event<FileChange[]> {
return this.onFileChangedEmitter.event;
}

get onFileChanges(): Event<FileChangesEvent> {
return this.onFileChangesEmitter.event;
onDidFilesChanged(event: DidFilesChangedParams): void {
const changes = event.changes.map(change => <FileChange>{
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<Disposable> {
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<FileChange[]> {
return this.listener.onFilesChanged;
}
}

2 changes: 2 additions & 0 deletions src/filesystem/common/filesystem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

import { Disposable } from '../../application/common';

export const fileSystemPath = '/filesystem';

export const FileSystem = Symbol("FileSystem");

export interface FileSystem extends Disposable {
Expand Down
6 changes: 3 additions & 3 deletions src/filesystem/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
33 changes: 27 additions & 6 deletions src/filesystem/node/filesystem-server-module.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

/*
* Copyright (C) 2017 TypeFox and others.
*
Expand All @@ -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<FileSystemWatcherClient>(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);
});
});
Loading

0 comments on commit c335a9d

Please sign in to comment.