watcherService.ts 3.1 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

8
import {TPromise} from 'vs/base/common/winjs.base';
B
Benjamin Pasero 已提交
9
import {Client} from 'vs/base/node/service.cp';
E
Erich Gamma 已提交
10 11 12 13 14 15 16 17 18 19 20 21
import uri from 'vs/base/common/uri';
import {EventType} from 'vs/platform/files/common/files';
import {toFileChangesEvent, IRawFileChange} from 'vs/workbench/services/files/node/watcher/common';
import {IEventService} from 'vs/platform/event/common/event';

export interface IWatcherRequest {
	basePath: string;
	ignored: string[];
	verboseLogging: boolean;
}

export class WatcherService {
22
	public watch(request: IWatcherRequest): TPromise<void> {
E
Erich Gamma 已提交
23 24 25 26 27
		throw new Error('not implemented');
	}
}

export class FileWatcher {
28 29
	private static MAX_RESTARTS = 5;

E
Erich Gamma 已提交
30
	private isDisposed: boolean;
31
	private restartCounter: number;
E
Erich Gamma 已提交
32 33 34

	constructor(private basePath: string, private ignored: string[], private eventEmitter: IEventService, private errorLogger: (msg: string) => void, private verboseLogging: boolean) {
		this.isDisposed = false;
35
		this.restartCounter = 0;
E
Erich Gamma 已提交
36 37
	}

38
	public startWatching(): () => void {
E
Erich Gamma 已提交
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
		const client = new Client(
			uri.parse(require.toUrl('bootstrap')).fsPath,
			{
				serverName: 'Watcher',
				args: ['--type=watcherService'],
				env: {
					AMD_ENTRYPOINT: 'vs/workbench/services/files/node/watcher/unix/watcherApp',
					PIPE_LOGGING: 'true',
					VERBOSE_LOGGING: this.verboseLogging
				}
			}
		);

		const service = client.getService<WatcherService>('WatcherService', WatcherService);

		// Start watching
		service.watch({ basePath: this.basePath, ignored: this.ignored, verboseLogging: this.verboseLogging }).then(null, (err) => {
			if (!(err instanceof Error && err.name === 'Canceled' && err.message === 'Canceled')) {
57
				return TPromise.wrapError(err); // the service lib uses the promise cancel error to indicate the process died, we do not want to bubble this up
E
Erich Gamma 已提交
58 59 60 61
			}
		}, (events: IRawFileChange[]) => this.onRawFileEvents(events)).done(() => {

			// our watcher app should never be completed because it keeps on watching. being in here indicates
62
			// that the watcher process died and we want to restart it here. we only do it a max number of times
E
Erich Gamma 已提交
63
			if (!this.isDisposed) {
64 65 66 67 68 69 70
				if (this.restartCounter <= FileWatcher.MAX_RESTARTS) {
					this.errorLogger('Watcher terminated unexpectedly and is restarted again...');
					this.restartCounter++;
					this.startWatching();
				} else {
					this.errorLogger('Watcher failed to start after retrying for some time, giving up. Please report this as a bug report!');
				}
E
Erich Gamma 已提交
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
			}
		}, this.errorLogger);

		return () => {
			client.dispose();
			this.isDisposed = true;
		};
	}

	private onRawFileEvents(events: IRawFileChange[]): void {

		// Emit through broadcast service
		if (events.length > 0) {
			this.eventEmitter.emit(EventType.FILE_CHANGES, toFileChangesEvent(events));
		}
	}
}