csharpWatcherService.ts 3.9 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 * as cp from 'child_process';
E
Erich Gamma 已提交
9

J
Johannes Rieken 已提交
10
import { FileChangeType } from 'vs/platform/files/common/files';
11 12
import * as decoder from 'vs/base/node/decoder';
import * as glob from 'vs/base/common/glob';
E
Erich Gamma 已提交
13

J
Johannes Rieken 已提交
14
import { IRawFileChange } from 'vs/workbench/services/files/node/watcher/common';
15
import { getPathFromAmdModule } from 'vs/base/common/amd';
E
Erich Gamma 已提交
16 17 18

export class OutOfProcessWin32FolderWatcher {

19
	private static readonly MAX_RESTARTS = 5;
20

E
Erich Gamma 已提交
21 22
	private static changeTypeMap: FileChangeType[] = [FileChangeType.UPDATED, FileChangeType.ADDED, FileChangeType.DELETED];

23 24
	private ignored: glob.ParsedPattern[];

E
Erich Gamma 已提交
25
	private handle: cp.ChildProcess;
26
	private restartCounter: number;
E
Erich Gamma 已提交
27

28 29
	constructor(
		private watchedFolder: string,
30
		ignored: string[],
31 32 33 34
		private eventCallback: (events: IRawFileChange[]) => void,
		private errorCallback: (error: string) => void,
		private verboseLogging: boolean
	) {
35 36
		this.restartCounter = 0;

37 38 39 40 41 42
		if (Array.isArray(ignored)) {
			this.ignored = ignored.map(i => glob.parse(i));
		} else {
			this.ignored = [];
		}

E
Erich Gamma 已提交
43 44 45 46
		this.startWatcher();
	}

	private startWatcher(): void {
B
Benjamin Pasero 已提交
47
		const args = [this.watchedFolder];
E
Erich Gamma 已提交
48 49 50 51
		if (this.verboseLogging) {
			args.push('-verbose');
		}

52
		this.handle = cp.spawn(getPathFromAmdModule(require, 'vs/workbench/services/files/node/watcher/win32/CodeHelper.exe'), args);
E
Erich Gamma 已提交
53

B
Benjamin Pasero 已提交
54
		const stdoutLineDecoder = new decoder.LineDecoder();
E
Erich Gamma 已提交
55 56

		// Events over stdout
B
Benjamin Pasero 已提交
57
		this.handle.stdout.on('data', (data: Buffer) => {
E
Erich Gamma 已提交
58 59

			// Collect raw events from output
B
Benjamin Pasero 已提交
60
			const rawEvents: IRawFileChange[] = [];
E
Erich Gamma 已提交
61
			stdoutLineDecoder.write(data).forEach((line) => {
B
Benjamin Pasero 已提交
62
				const eventParts = line.split('|');
E
Erich Gamma 已提交
63
				if (eventParts.length === 2) {
B
Benjamin Pasero 已提交
64 65
					const changeType = Number(eventParts[0]);
					const absolutePath = eventParts[1];
E
Erich Gamma 已提交
66 67 68 69 70

					// File Change Event (0 Changed, 1 Created, 2 Deleted)
					if (changeType >= 0 && changeType < 3) {

						// Support ignores
71
						if (this.ignored && this.ignored.some(ignore => ignore(absolutePath))) {
72 73 74 75
							if (this.verboseLogging) {
								console.log('%c[File Watcher (C#)]', 'color: blue', ' >> ignored', absolutePath);
							}

E
Erich Gamma 已提交
76 77 78 79 80 81 82 83 84 85 86 87
							return;
						}

						// Otherwise record as event
						rawEvents.push({
							type: OutOfProcessWin32FolderWatcher.changeTypeMap[changeType],
							path: absolutePath
						});
					}

					// 3 Logging
					else {
88
						console.log('%c[File Watcher (C#)]', 'color: blue', eventParts[1]);
E
Erich Gamma 已提交
89 90 91 92 93 94 95 96 97 98 99 100
					}
				}
			});

			// Trigger processing of events through the delayer to batch them up properly
			if (rawEvents.length > 0) {
				this.eventCallback(rawEvents);
			}
		});

		// Errors
		this.handle.on('error', (error: Error) => this.onError(error));
B
Benjamin Pasero 已提交
101
		this.handle.stderr.on('data', (data: Buffer) => this.onError(data));
E
Erich Gamma 已提交
102 103

		// Exit
B
Benjamin Pasero 已提交
104
		this.handle.on('exit', (code: number, signal: string) => this.onExit(code, signal));
E
Erich Gamma 已提交
105 106
	}

B
Benjamin Pasero 已提交
107
	private onError(error: Error | Buffer): void {
108
		this.errorCallback('[File Watcher (C#)] process error: ' + error.toString());
E
Erich Gamma 已提交
109 110
	}

B
Benjamin Pasero 已提交
111
	private onExit(code: number, signal: string): void {
E
Erich Gamma 已提交
112
		if (this.handle) { // exit while not yet being disposed is unexpected!
113
			this.errorCallback(`[File Watcher (C#)] terminated unexpectedly (code: ${code}, signal: ${signal})`);
114 115

			if (this.restartCounter <= OutOfProcessWin32FolderWatcher.MAX_RESTARTS) {
116
				this.errorCallback('[File Watcher (C#)] is restarted again...');
117 118 119
				this.restartCounter++;
				this.startWatcher(); // restart
			} else {
120
				this.errorCallback('[File Watcher (C#)] Watcher failed to start after retrying for some time, giving up. Please report this as a bug report!');
121
			}
E
Erich Gamma 已提交
122 123 124 125 126 127 128 129 130
		}
	}

	public dispose(): void {
		if (this.handle) {
			this.handle.kill();
			this.handle = null;
		}
	}
131
}