private _watch()

in src/vs/platform/files/node/watcher/nsfw/nsfwWatcherService.ts [52:177]


	private _watch(request: IWatcherRequest): void {
		let undeliveredFileEvents: IDiskFileChange[] = [];
		const fileEventDelayer = new ThrottledDelayer<void>(NsfwWatcherService.FS_EVENT_DELAY);

		let readyPromiseResolve: (watcher: IWatcherObjet) => void;
		this._pathWatchers[request.path] = {
			ready: new Promise<IWatcherObjet>(resolve => readyPromiseResolve = resolve),
			ignored: Array.isArray(request.excludes) ? request.excludes.map(ignored => glob.parse(ignored)) : []
		};

		process.on('uncaughtException', (e: Error | string) => {

			// Specially handle ENOSPC errors that can happen when
			// the watcher consumes so many file descriptors that
			// we are running into a limit. We only want to warn
			// once in this case to avoid log spam.
			// See https://github.com/Microsoft/vscode/issues/7950
			if (e === 'Inotify limit reached' && !this.enospcErrorLogged) {
				this.enospcErrorLogged = true;
				this.error('Inotify limit reached (ENOSPC)');
			}
		});

		// NSFW does not report file changes in the path provided on macOS if
		// - the path uses wrong casing
		// - the path is a symbolic link
		// We have to detect this case and massage the events to correct this.
		let realBasePathDiffers = false;
		let realBasePathLength = request.path.length;
		if (platform.isMacintosh) {
			try {

				// First check for symbolic link
				let realBasePath = realpathSync(request.path);

				// Second check for casing difference
				if (request.path === realBasePath) {
					realBasePath = (realcaseSync(request.path) || request.path);
				}

				if (request.path !== realBasePath) {
					realBasePathLength = realBasePath.length;
					realBasePathDiffers = true;

					this.warn(`Watcher basePath does not match version on disk and will be corrected (original: ${request.path}, real: ${realBasePath})`);
				}
			} catch (error) {
				// ignore
			}
		}

		nsfw(request.path, events => {
			for (const e of events) {
				// Logging
				if (this._verboseLogging) {
					const logPath = e.action === nsfw.actions.RENAMED ? path.join(e.directory, e.oldFile || '') + ' -> ' + e.newFile : path.join(e.directory, e.file || '');
					this.log(`${e.action === nsfw.actions.CREATED ? '[CREATED]' : e.action === nsfw.actions.DELETED ? '[DELETED]' : e.action === nsfw.actions.MODIFIED ? '[CHANGED]' : '[RENAMED]'} ${logPath}`);
				}

				// Convert nsfw event to IRawFileChange and add to queue
				let absolutePath: string;
				if (e.action === nsfw.actions.RENAMED) {
					// Rename fires when a file's name changes within a single directory
					absolutePath = path.join(e.directory, e.oldFile || '');
					if (!this._isPathIgnored(absolutePath, this._pathWatchers[request.path].ignored)) {
						undeliveredFileEvents.push({ type: FileChangeType.DELETED, path: absolutePath });
					} else if (this._verboseLogging) {
						this.log(` >> ignored ${absolutePath}`);
					}
					absolutePath = path.join(e.newDirectory || e.directory, e.newFile || '');
					if (!this._isPathIgnored(absolutePath, this._pathWatchers[request.path].ignored)) {
						undeliveredFileEvents.push({ type: FileChangeType.ADDED, path: absolutePath });
					} else if (this._verboseLogging) {
						this.log(` >> ignored ${absolutePath}`);
					}
				} else {
					absolutePath = path.join(e.directory, e.file || '');
					if (!this._isPathIgnored(absolutePath, this._pathWatchers[request.path].ignored)) {
						undeliveredFileEvents.push({
							type: nsfwActionToRawChangeType[e.action],
							path: absolutePath
						});
					} else if (this._verboseLogging) {
						this.log(` >> ignored ${absolutePath}`);
					}
				}
			}

			// Delay and send buffer
			fileEventDelayer.trigger(() => {
				const events = undeliveredFileEvents;
				undeliveredFileEvents = [];

				if (platform.isMacintosh) {
					events.forEach(e => {

						// Mac uses NFD unicode form on disk, but we want NFC
						e.path = normalizeNFC(e.path);

						// Convert paths back to original form in case it differs
						if (realBasePathDiffers) {
							e.path = request.path + e.path.substr(realBasePathLength);
						}
					});
				}

				// Broadcast to clients normalized
				const res = normalizeFileChanges(events);
				this._onWatchEvent.fire(res);

				// Logging
				if (this._verboseLogging) {
					res.forEach(r => {
						this.log(` >> normalized ${r.type === FileChangeType.ADDED ? '[ADDED]' : r.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${r.path}`);
					});
				}

				return Promise.resolve(undefined);
			});
		}).then(watcher => {
			this._pathWatchers[request.path].watcher = watcher;
			const startPromise = watcher.start();
			startPromise.then(() => readyPromiseResolve(watcher));
			return startPromise;
		});
	}