watcher - remove vscode-nsfw legacy watcher

This commit is contained in:
Benjamin Pasero 2021-12-06 10:50:15 +01:00
parent 8d61a33e38
commit 16d0a319b2
No known key found for this signature in database
GPG key ID: C035C296C8A46619
32 changed files with 239 additions and 946 deletions

View file

@ -78,12 +78,6 @@ node-pty/scripts/**
!node-pty/build/Release/*.dll
!node-pty/build/Release/*.node
vscode-nsfw/binding.gyp
vscode-nsfw/build/**
vscode-nsfw/src/**
vscode-nsfw/includes/**
!vscode-nsfw/build/Release/*.node
@parcel/watcher/binding.gyp
@parcel/watcher/build/**
@parcel/watcher/prebuilds/**

View file

@ -103,11 +103,7 @@ const serverEntryPoints = [
exclude: ['vs/css', 'vs/nls']
},
{
name: 'vs/platform/files/node/watcher/nsfw/watcherApp',
exclude: ['vs/css', 'vs/nls']
},
{
name: 'vs/platform/files/node/watcher/parcel/watcherApp',
name: 'vs/platform/files/node/watcher/parcel/parcelWatcherMain',
exclude: ['vs/css', 'vs/nls']
},
{

View file

@ -79,7 +79,6 @@
"spdlog": "^0.13.0",
"tas-client-umd": "0.1.4",
"v8-inspect-profiler": "^0.0.22",
"vscode-nsfw": "2.1.8",
"vscode-oniguruma": "1.6.1",
"vscode-proxy-agent": "^0.11.0",
"vscode-regexpp": "^3.1.0",

View file

@ -18,7 +18,6 @@
"node-pty": "0.11.0-beta11",
"spdlog": "^0.13.0",
"tas-client-umd": "0.1.4",
"vscode-nsfw": "2.1.8",
"vscode-oniguruma": "1.6.1",
"vscode-proxy-agent": "^0.11.0",
"vscode-regexpp": "^3.1.0",

View file

@ -384,11 +384,6 @@ node-addon-api@^3.2.1:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
node-addon-api@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87"
integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q==
node-gyp-build@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3"
@ -472,13 +467,6 @@ universalify@^0.1.0:
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
vscode-nsfw@2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-2.1.8.tgz#88f5e56b22b2fd0be582e73eb1158ea8257f6c6c"
integrity sha512-tFnxPIuM65czw/Kjz8KXD88fIJtnCjzQ0ighS0a1yasVv6jKkANAlGffiOitTLMkDjvFCY8OyP6xjarTkpu/VQ==
dependencies:
node-addon-api "^4.2.0"
vscode-oniguruma@1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.6.1.tgz#2bf4dfcfe3dd2e56eb549a3068c8ee39e6c30ce5"

View file

@ -22,7 +22,6 @@ export class HTMLFileSystemProvider implements IFileSystemProviderWithFileReadWr
readonly onDidChangeCapabilities = Event.None;
readonly onDidChangeFile = Event.None;
readonly onDidErrorOccur = Event.None;
//#endregion

View file

@ -11,7 +11,7 @@ import { combinedDisposable, Disposable, IDisposable, toDisposable } from 'vs/ba
import { normalize } from 'vs/base/common/path';
import { URI } from 'vs/base/common/uri';
import { IFileChange, IWatchOptions } from 'vs/platform/files/common/files';
import { IDiskFileChange, ILogMessage, IWatchRequest, toFileChanges, WatcherService } from 'vs/platform/files/common/watcher';
import { AbstractRecursiveWatcherClient, IDiskFileChange, ILogMessage, IWatchRequest, toFileChanges } from 'vs/platform/files/common/watcher';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
export abstract class AbstractDiskFileSystemProvider extends Disposable {
@ -24,13 +24,13 @@ export abstract class AbstractDiskFileSystemProvider extends Disposable {
//#region File Watching
protected readonly _onDidErrorOccur = this._register(new Emitter<string>());
readonly onDidErrorOccur = this._onDidErrorOccur.event;
protected readonly _onDidChangeFile = this._register(new Emitter<readonly IFileChange[]>());
readonly onDidChangeFile = this._onDidChangeFile.event;
private recursiveWatcher: WatcherService | undefined;
protected readonly _onDidWatchError = this._register(new Emitter<string>());
readonly onDidWatchError = this._onDidWatchError.event;
private recursiveWatcher: AbstractRecursiveWatcherClient | undefined;
private readonly recursiveFoldersToWatch: IWatchRequest[] = [];
private recursiveWatchRequestDelayer = this._register(new ThrottledDelayer<void>(0));
@ -90,7 +90,7 @@ export abstract class AbstractDiskFileSystemProvider extends Disposable {
return this.doWatch(this.recursiveWatcher, this.recursiveFoldersToWatch);
}
protected doWatch(watcher: WatcherService, requests: IWatchRequest[]): Promise<void> {
protected doWatch(watcher: AbstractRecursiveWatcherClient, requests: IWatchRequest[]): Promise<void> {
return watcher.watch(requests);
}
@ -98,10 +98,10 @@ export abstract class AbstractDiskFileSystemProvider extends Disposable {
onChange: (changes: IDiskFileChange[]) => void,
onLogMessage: (msg: ILogMessage) => void,
verboseLogging: boolean
): WatcherService;
): AbstractRecursiveWatcherClient;
private watchNonRecursive(resource: URI): IDisposable {
const watcherService = this.createNonRecursiveWatcher(
const watcher = this.createNonRecursiveWatcher(
this.toFilePath(resource),
changes => this._onDidChangeFile.fire(toFileChanges(changes)),
msg => this.onWatcherLogMessage(msg),
@ -109,15 +109,15 @@ export abstract class AbstractDiskFileSystemProvider extends Disposable {
);
const logLevelListener = this.logService.onDidChangeLogLevel(() => {
watcherService.setVerboseLogging(this.logService.getLevel() === LogLevel.Trace);
watcher.setVerboseLogging(this.logService.getLevel() === LogLevel.Trace);
});
return combinedDisposable(watcherService, logLevelListener);
return combinedDisposable(watcher, logLevelListener);
}
private onWatcherLogMessage(msg: ILogMessage): void {
if (msg.type === 'error') {
this._onDidErrorOccur.fire(msg.message);
this._onDidWatchError.fire(msg.message);
}
this.logService[msg.type](msg.message);

View file

@ -62,8 +62,8 @@ export class FileService extends Disposable implements IFileService {
const providerDisposables = new DisposableStore();
providerDisposables.add(provider.onDidChangeFile(changes => this.onDidChangeFile(changes, this.isPathCaseSensitive(provider))));
providerDisposables.add(provider.onDidChangeCapabilities(() => this._onDidChangeFileSystemProviderCapabilities.fire({ provider, scheme })));
if (typeof provider.onDidErrorOccur === 'function') {
providerDisposables.add(provider.onDidErrorOccur(error => this._onError.fire(new Error(error))));
if (typeof provider.onDidWatchError === 'function') {
providerDisposables.add(provider.onDidWatchError(error => this._onDidWatchError.fire(new Error(error))));
}
return toDisposable(() => {
@ -166,11 +166,12 @@ export class FileService extends Disposable implements IFileService {
//#endregion
//#region Operation events
private readonly _onDidRunOperation = this._register(new Emitter<FileOperationEvent>());
readonly onDidRunOperation = this._onDidRunOperation.event;
private readonly _onError = this._register(new Emitter<Error>());
readonly onError = this._onError.event;
//#endregion
//#region File Metadata Resolving
@ -993,6 +994,9 @@ export class FileService extends Disposable implements IFileService {
private readonly _onDidFilesChange = this._register(new Emitter<FileChangesEvent>());
readonly onDidFilesChange = this._onDidFilesChange.event;
private readonly _onDidWatchError = this._register(new Emitter<Error>());
readonly onDidWatchError = this._onDidWatchError.event;
private readonly _onDidChangeFilesRaw = this._register(new Emitter<IRawFileChangesEvent>());
readonly onDidChangeFilesRaw = this._onDidChangeFilesRaw.event;

View file

@ -435,9 +435,8 @@ export interface IFileSystemProvider {
readonly capabilities: FileSystemProviderCapabilities;
readonly onDidChangeCapabilities: Event<void>;
readonly onDidErrorOccur?: Event<string>;
readonly onDidChangeFile: Event<readonly IFileChange[]>;
readonly onDidWatchError?: Event<string>;
watch(resource: URI, opts: IWatchOptions): IDisposable;
stat(resource: URI): Promise<IStat>;

View file

@ -186,8 +186,8 @@ export class IPCFileSystemProvider extends Disposable implements
private readonly _onDidChange = this._register(new Emitter<readonly IFileChange[]>());
readonly onDidChangeFile = this._onDidChange.event;
private readonly _onDidErrorOccur = this._register(new Emitter<string>());
readonly onDidErrorOccur = this._onDidErrorOccur.event;
private readonly _onDidWatchError = this._register(new Emitter<string>());
readonly onDidWatchError = this._onDidWatchError.event;
// The contract for file watching via remote is to identify us
// via a unique but readonly session ID. Since the remote is
@ -208,7 +208,7 @@ export class IPCFileSystemProvider extends Disposable implements
this._onDidChange.fire(events.map(event => ({ resource: URI.revive(event.resource), type: event.type })));
} else {
const error = eventsOrError;
this._onDidErrorOccur.fire(error);
this._onDidWatchError.fire(error);
}
}));
}

View file

@ -9,7 +9,7 @@ import { isLinux } from 'vs/base/common/platform';
import { URI as uri } from 'vs/base/common/uri';
import { FileChangeType, IFileChange, isParent } from 'vs/platform/files/common/files';
export interface IWatcherService {
export interface IRecursiveWatcher {
/**
* A normalized file change event from the raw events
@ -25,15 +25,15 @@ export interface IWatcherService {
/**
* An event to indicate an error occured from the watcher
* that is unrecoverable. Listeners should restart the
* service if possible.
* watcher if possible.
*/
readonly onDidError: Event<string>;
/**
* Configures the watcher service to watch according
* to the requests. Any existing watched path that
* is not in the array, will be removed from watching
* and any new path will be added to watching.
* Configures the watcher to watch according to the
* requests. Any existing watched path that is not
* in the array, will be removed from watching and
* any new path will be added to watching.
*/
watch(requests: IWatchRequest[]): Promise<void>;
@ -48,12 +48,12 @@ export interface IWatcherService {
stop(): Promise<void>;
}
export abstract class AbstractWatcherService extends Disposable {
export abstract class AbstractRecursiveWatcherClient extends Disposable {
private static readonly MAX_RESTARTS = 5;
private service: IWatcherService | undefined;
private readonly serviceDisposables = this._register(new MutableDisposable());
private watcher: IRecursiveWatcher | undefined;
private readonly watcherDisposables = this._register(new MutableDisposable());
private requests: IWatchRequest[] | undefined = undefined;
@ -67,28 +67,28 @@ export abstract class AbstractWatcherService extends Disposable {
super();
}
protected abstract createService(disposables: DisposableStore): IWatcherService;
protected abstract createWatcher(disposables: DisposableStore): IRecursiveWatcher;
protected init(): void {
// Associate disposables to the service
// Associate disposables to the watcher
const disposables = new DisposableStore();
this.serviceDisposables.value = disposables;
this.watcherDisposables.value = disposables;
// Ask implementors to create the service
this.service = this.createService(disposables);
this.service.setVerboseLogging(this.verboseLogging);
// Ask implementors to create the watcher
this.watcher = this.createWatcher(disposables);
this.watcher.setVerboseLogging(this.verboseLogging);
// Wire in event handlers
disposables.add(this.service.onDidChangeFile(e => this.onFileChanges(e)));
disposables.add(this.service.onDidLogMessage(e => this.onLogMessage(e)));
disposables.add(this.service.onDidError(e => this.onError(e)));
disposables.add(this.watcher.onDidChangeFile(e => this.onFileChanges(e)));
disposables.add(this.watcher.onDidLogMessage(e => this.onLogMessage(e)));
disposables.add(this.watcher.onDidError(e => this.onError(e)));
}
protected onError(error: string): void {
// Restart up to N times
if (this.restartCounter < AbstractWatcherService.MAX_RESTARTS && this.requests) {
if (this.restartCounter < AbstractRecursiveWatcherClient.MAX_RESTARTS && this.requests) {
this.error(`restarting watcher after error: ${error}`);
this.restart(this.requests);
}
@ -109,13 +109,13 @@ export abstract class AbstractWatcherService extends Disposable {
async watch(requests: IWatchRequest[]): Promise<void> {
this.requests = requests;
await this.service?.watch(requests);
await this.watcher?.watch(requests);
}
async setVerboseLogging(verboseLogging: boolean): Promise<void> {
this.verboseLogging = verboseLogging;
await this.service?.setVerboseLogging(verboseLogging);
await this.watcher?.setVerboseLogging(verboseLogging);
}
private error(message: string) {
@ -124,31 +124,13 @@ export abstract class AbstractWatcherService extends Disposable {
override dispose(): void {
// Render the serve invalid from here
this.service = undefined;
// Render the watcher invalid from here
this.watcher = undefined;
return super.dispose();
}
}
/**
* Base class of any watcher service we support.
*
* TODO@bpasero delete and replace with `AbstractWatcherService`
*/
export abstract class WatcherService extends Disposable {
/**
* Asks to watch the provided folders.
*/
abstract watch(requests: IWatchRequest[]): Promise<void>;
/**
* Enable verbose logging from the watcher.
*/
abstract setVerboseLogging(verboseLogging: boolean): Promise<void>;
}
export interface IWatchRequest {
/**

View file

@ -9,7 +9,7 @@ import { isWindows } from 'vs/base/common/platform';
import { Emitter } from 'vs/base/common/event';
import { URI, UriComponents } from 'vs/base/common/uri';
import { FileDeleteOptions, IFileChange, IWatchOptions, createFileSystemProviderError, FileSystemProviderErrorCode } from 'vs/platform/files/common/files';
import { FileWatcher as NodeJSWatcherService } from 'vs/platform/files/node/watcher/nodejs/watcherService';
import { NodeJSFileWatcher } from 'vs/platform/files/node/watcher/nodejs/nodejsWatcher';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { basename, normalize } from 'vs/base/common/path';
import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
@ -88,7 +88,7 @@ class SessionFileWatcher extends Disposable implements ISessionFileWatcher {
this.watcherRequests.set(req, disposable);
disposable.add(toDisposable(() => this.watcherRequests.delete(req)));
const watcher = disposable.add(new NodeJSWatcherService(
const watcher = disposable.add(new NodeJSFileWatcher(
normalize(resource.fsPath),
changes => this.sessionEmitter.fire(toFileChanges(changes)),
msg => this.onWatcherLogMessage(msg),

View file

@ -20,10 +20,9 @@ import { IDirent, Promises, RimRafMode, SymlinkSupport } from 'vs/base/node/pfs'
import { localize } from 'vs/nls';
import { createFileSystemProviderError, FileDeleteOptions, FileOpenOptions, FileOverwriteOptions, FileReadStreamOptions, FileSystemProviderCapabilities, FileSystemProviderError, FileSystemProviderErrorCode, FileType, FileWriteOptions, IFileSystemProviderWithFileFolderCopyCapability, IFileSystemProviderWithFileReadStreamCapability, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithOpenReadWriteCloseCapability, isFileOpenForWriteOptions, IStat } from 'vs/platform/files/common/files';
import { readFileIntoStream } from 'vs/platform/files/common/io';
import { FileWatcher as NodeJSWatcherService } from 'vs/platform/files/node/watcher/nodejs/watcherService';
import { FileWatcher as NsfwWatcherService } from 'vs/platform/files/node/watcher/nsfw/watcherService';
import { FileWatcher as ParcelWatcherService } from 'vs/platform/files/node/watcher/parcel/watcherService';
import { IDiskFileChange, ILogMessage, IWatchRequest, WatcherService } from 'vs/platform/files/common/watcher';
import { NodeJSFileWatcher } from 'vs/platform/files/node/watcher/nodejs/nodejsWatcher';
import { ParcelWatcherClient } from 'vs/platform/files/node/watcher/parcel/parcelWatcherClient';
import { AbstractRecursiveWatcherClient, IDiskFileChange, ILogMessage, IWatchRequest } from 'vs/platform/files/common/watcher';
import { ILogService } from 'vs/platform/log/common/log';
import { AbstractDiskFileSystemProvider } from 'vs/platform/files/common/diskFileSystemProvider';
import { toErrorMessage } from 'vs/base/common/errorMessage';
@ -63,7 +62,6 @@ export interface IWatcherOptions {
export interface IDiskFileSystemProviderOptions {
watcher?: IWatcherOptions;
legacyWatcher?: string;
}
export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider implements
@ -551,38 +549,15 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple
onChange: (changes: IDiskFileChange[]) => void,
onLogMessage: (msg: ILogMessage) => void,
verboseLogging: boolean
): WatcherService {
let watcherImpl: {
new(
onChange: (changes: IDiskFileChange[]) => void,
onLogMessage: (msg: ILogMessage) => void,
verboseLogging: boolean,
watcherOptions?: IWatcherOptions
): WatcherService
};
let enableLegacyWatcher = false;
if (this.options?.watcher?.usePolling) {
enableLegacyWatcher = false; // must use Parcel watcher for when polling is required
} else {
enableLegacyWatcher = this.options?.legacyWatcher === 'on'; // setting always wins
}
if (enableLegacyWatcher) {
watcherImpl = NsfwWatcherService;
} else {
watcherImpl = ParcelWatcherService;
}
return new watcherImpl(
): AbstractRecursiveWatcherClient {
return new ParcelWatcherClient(
changes => onChange(changes),
msg => onLogMessage(msg),
verboseLogging,
this.options?.watcher
verboseLogging
);
}
protected override doWatch(watcher: WatcherService, requests: IWatchRequest[]): Promise<void> {
protected override doWatch(watcher: AbstractRecursiveWatcherClient, requests: IWatchRequest[]): Promise<void> {
const usePolling = this.options?.watcher?.usePolling;
if (usePolling === true) {
for (const request of requests) {
@ -605,7 +580,7 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple
onLogMessage: (msg: ILogMessage) => void,
verboseLogging: boolean
): IDisposable & { setVerboseLogging: (verboseLogging: boolean) => void } {
return new NodeJSWatcherService(
return new NodeJSFileWatcher(
path,
changes => onChange(changes),
msg => onLogMessage(msg),

View file

@ -12,11 +12,12 @@ import { CHANGE_BUFFER_DELAY, watchFile, watchFolder } from 'vs/base/node/watche
import { FileChangeType } from 'vs/platform/files/common/files';
import { IDiskFileChange, ILogMessage, coalesceEvents } from 'vs/platform/files/common/watcher';
export class FileWatcher extends Disposable {
private isDisposed: boolean | undefined;
export class NodeJSFileWatcher extends Disposable {
private readonly fileChangesDelayer: ThrottledDelayer<void> = this._register(new ThrottledDelayer<void>(CHANGE_BUFFER_DELAY * 2 /* sync on delay from underlying library */));
private fileChangesBuffer: IDiskFileChange[] = [];
private isDisposed: boolean | undefined;
constructor(
private path: string,

View file

@ -1,478 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nsfw from 'vscode-nsfw';
import { existsSync } from 'fs';
import { RunOnceScheduler } from 'vs/base/common/async';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { Emitter } from 'vs/base/common/event';
import { parse, ParsedPattern } from 'vs/base/common/glob';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { TernarySearchTree } from 'vs/base/common/map';
import { normalizeNFC } from 'vs/base/common/normalization';
import { dirname, join } from 'vs/base/common/path';
import { isMacintosh } from 'vs/base/common/platform';
import { realcaseSync, realpathSync } from 'vs/base/node/extpath';
import { FileChangeType } from 'vs/platform/files/common/files';
import { IWatcherService } from 'vs/platform/files/node/watcher/nsfw/watcher';
import { IDiskFileChange, ILogMessage, coalesceEvents, IWatchRequest } from 'vs/platform/files/common/watcher';
import { watchFolder } from 'vs/base/node/watcher';
interface IWatcher extends IDisposable {
/**
* The NSFW instance is resolved when the watching has started.
*/
readonly instance: Promise<nsfw.NSFW>;
/**
* The watch request associated to the watcher.
*/
request: IWatchRequest;
/**
* Associated ignored patterns for the watcher that can be updated.
*/
ignored: ParsedPattern[];
/**
* How often this watcher has been restarted in case of an unexpected
* shutdown.
*/
restarts: number;
/**
* The cancellation token associated with the lifecycle of the watcher.
*/
token: CancellationToken;
}
export class NsfwWatcherService extends Disposable implements IWatcherService {
private static readonly MAX_RESTARTS = 5; // number of restarts we allow before giving up in case of unexpected shutdown
private static readonly MAP_NSFW_ACTION_TO_FILE_CHANGE = new Map<number, number>(
[
[nsfw.actions.CREATED, FileChangeType.ADDED],
[nsfw.actions.MODIFIED, FileChangeType.UPDATED],
[nsfw.actions.DELETED, FileChangeType.DELETED],
]
);
private readonly _onDidChangeFile = this._register(new Emitter<IDiskFileChange[]>());
readonly onDidChangeFile = this._onDidChangeFile.event;
private readonly _onDidLogMessage = this._register(new Emitter<ILogMessage>());
readonly onDidLogMessage = this._onDidLogMessage.event;
protected readonly watchers = new Map<string, IWatcher>();
private verboseLogging = false;
private enospcErrorLogged = false;
constructor() {
super();
this.registerListeners();
}
private registerListeners(): void {
// Error handling on process
process.on('uncaughtException', error => this.onError(error));
process.on('unhandledRejection', error => this.onError(error));
}
async watch(requests: IWatchRequest[]): Promise<void> {
// Figure out duplicates to remove from the requests
const normalizedRequests = this.normalizeRequests(requests);
// Gather paths that we should start watching
const requestsToStartWatching = normalizedRequests.filter(request => {
return !this.watchers.has(request.path);
});
// Gather paths that we should stop watching
const pathsToStopWatching = Array.from(this.watchers.keys()).filter(watchedPath => {
return !normalizedRequests.find(normalizedRequest => normalizedRequest.path === watchedPath);
});
// Logging
this.debug(`Request to start watching: ${requestsToStartWatching.map(request => `${request.path} (excludes: ${request.excludes})`).join(',')}`);
this.debug(`Request to stop watching: ${pathsToStopWatching.join(',')}`);
// Stop watching as instructed
for (const pathToStopWatching of pathsToStopWatching) {
this.stopWatching(pathToStopWatching);
}
// Start watching as instructed
for (const request of requestsToStartWatching) {
this.startWatching(request);
}
// Update ignore rules for all watchers
for (const request of normalizedRequests) {
const watcher = this.watchers.get(request.path);
if (watcher) {
watcher.request = request;
watcher.ignored = this.toExcludePatterns(request.excludes);
}
}
}
private toExcludePatterns(excludes: string[] | undefined): ParsedPattern[] {
return Array.isArray(excludes) ? excludes.map(exclude => parse(exclude)) : [];
}
private startWatching(request: IWatchRequest, restarts = 0): void {
const cts = new CancellationTokenSource();
let nsfwPromiseResolve: (watcher: nsfw.NSFW) => void;
const instance = new Promise<nsfw.NSFW>(resolve => nsfwPromiseResolve = resolve);
// Remember as watcher instance
const watcher: IWatcher = {
request,
instance,
ignored: this.toExcludePatterns(request.excludes),
restarts,
token: cts.token,
dispose: () => {
cts.dispose(true);
instance.then(instance => instance.stop());
}
};
this.watchers.set(request.path, watcher);
// Path checks for symbolic links / wrong casing
const { realBasePathDiffers, realBasePathLength } = this.checkRequest(request);
let undeliveredFileEvents: IDiskFileChange[] = [];
const onRawFileEvent = (path: string, type: FileChangeType) => {
if (!this.isPathIgnored(path, watcher.ignored)) {
undeliveredFileEvents.push({ type, path });
} else if (this.verboseLogging) {
this.log(` >> ignored ${path}`);
}
};
nsfw(request.path, events => {
if (watcher.token.isCancellationRequested) {
return; // return early when disposed
}
for (const event of events) {
// Log the raw event before normalization or checking for ignore patterns
if (this.verboseLogging) {
const logPath = event.action === nsfw.actions.RENAMED ? `${join(event.directory, event.oldFile || '')} -> ${event.newFile}` : join(event.directory, event.file || '');
this.log(`${event.action === nsfw.actions.CREATED ? '[ADDED]' : event.action === nsfw.actions.DELETED ? '[DELETED]' : event.action === nsfw.actions.MODIFIED ? '[CHANGED]' : '[RENAMED]'} ${logPath}`);
}
// Rename: convert into DELETE & ADD
if (event.action === nsfw.actions.RENAMED) {
onRawFileEvent(join(event.directory, event.oldFile || ''), FileChangeType.DELETED); // Rename fires when a file's name changes within a single directory
onRawFileEvent(join(event.newDirectory || event.directory, event.newFile || ''), FileChangeType.ADDED);
}
// Created, modified, deleted: take as is
else {
onRawFileEvent(join(event.directory, event.file || ''), NsfwWatcherService.MAP_NSFW_ACTION_TO_FILE_CHANGE.get(event.action)!);
}
}
// Reset undelivered events array
const undeliveredFileEventsToEmit = undeliveredFileEvents;
undeliveredFileEvents = [];
// Broadcast to clients normalized
const normalizedEvents = coalesceEvents(this.normalizeEvents(undeliveredFileEventsToEmit, request, realBasePathDiffers, realBasePathLength));
this.emitEvents(normalizedEvents);
}, this.getOptions(watcher)).then(async nsfwWatcher => {
// Begin watching unless disposed already
if (!watcher.token.isCancellationRequested) {
await nsfwWatcher.start();
}
return nsfwWatcher;
}).then(nsfwWatcher => {
this.debug(`Started watching: ${request.path}`);
nsfwPromiseResolve(nsfwWatcher);
});
}
protected getOptions(watcher: IWatcher): nsfw.Options {
return {
// We must install an error callback, otherwise any error
// that is thrown from the watcher will result in process exit
errorCallback: error => {
if (!watcher.token.isCancellationRequested) {
this.onError(error, watcher); // error handling only if we are not disposed yet
}
},
// The default delay of NSFW is 500 but we want to
// react a bit faster than that.
debounceMS: 250
};
}
private emitEvents(events: IDiskFileChange[]): void {
// Send outside
this._onDidChangeFile.fire(events);
// Logging
if (this.verboseLogging) {
for (const event of events) {
this.log(` >> normalized ${event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${event.path}`);
}
}
}
private checkRequest(request: IWatchRequest): { realBasePathDiffers: boolean, realBasePathLength: number } {
let realBasePathDiffers = false;
let realBasePathLength = request.path.length;
// macOS: nsfw will report paths in their dereferenced and real casing
// form, so we need to detect this early on to be able to rewrite the
// file events to the original requested form.
// Note: Other platforms do not seem to have these path issues.
if (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(`correcting a path to watch that seems to be a symbolic link (original: ${request.path}, real: ${realBasePath})`);
}
} catch (error) {
// ignore
}
}
return { realBasePathDiffers, realBasePathLength };
}
private normalizeEvents(events: IDiskFileChange[], request: IWatchRequest, realBasePathDiffers: boolean, realBasePathLength: number): IDiskFileChange[] {
if (isMacintosh) {
for (const event of events) {
// Mac uses NFD unicode form on disk, but we want NFC
event.path = normalizeNFC(event.path);
// Convert paths back to original form in case it differs
if (realBasePathDiffers) {
event.path = request.path + event.path.substr(realBasePathLength);
}
}
}
return events;
}
private onError(error: unknown, watcher?: IWatcher): void {
const msg = toErrorMessage(error);
// 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 (msg.indexOf('Inotify limit reached') !== -1) {
if (!this.enospcErrorLogged) {
this.error('Inotify limit reached (ENOSPC)', watcher);
this.enospcErrorLogged = true;
}
}
// Any other error is unexpected and we should try to
// restart the watcher as a result to get into healthy
// state again.
else {
const handled = this.onUnexpectedError(msg, watcher);
if (!handled) {
this.error(`Unexpected error: ${msg} (EUNKNOWN)`, watcher);
}
}
}
private onUnexpectedError(error: string, watcher?: IWatcher): boolean {
if (!watcher || watcher.restarts >= NsfwWatcherService.MAX_RESTARTS) {
return false; // we need a watcher that has not been restarted MAX_RESTARTS times already
}
let handled = false;
// Just try to restart watcher now if the path still exists
if (existsSync(watcher.request.path)) {
this.warn(`Watcher will be restarted due to unexpected error: ${error}`, watcher);
this.restartWatching(watcher);
handled = true;
}
// Otherwise try to monitor the path coming back before
// restarting the watcher
else {
handled = this.onWatchedPathDeleted(watcher);
}
return handled;
}
private onWatchedPathDeleted(watcher: IWatcher): boolean {
this.warn('Watcher shutdown because watched path got deleted', watcher);
// Send a manual event given we know the root got deleted
this.emitEvents([{ path: watcher.request.path, type: FileChangeType.DELETED }]);
const parentPath = dirname(watcher.request.path);
if (existsSync(parentPath)) {
const disposable = watchFolder(parentPath, (type, path) => {
if (watcher.token.isCancellationRequested) {
return; // return early when disposed
}
// Watcher path came back! Restart watching...
if (path === watcher.request.path && (type === 'added' || type === 'changed')) {
this.warn('Watcher restarts because watched path got created again', watcher);
// Stop watching that parent folder
disposable.dispose();
// Send a manual event given we know the root got added again
this.emitEvents([{ path: watcher.request.path, type: FileChangeType.ADDED }]);
// Restart the file watching delayed
this.restartWatching(watcher);
}
}, error => {
// Ignore
});
// Make sure to stop watching when the watcher is disposed
watcher.token.onCancellationRequested(() => disposable.dispose());
return true; // handled
}
return false; // not handled
}
async stop(): Promise<void> {
for (const [path] of this.watchers) {
this.stopWatching(path);
}
this.watchers.clear();
}
private restartWatching(watcher: IWatcher, delay = 800): void {
// Restart watcher delayed to accomodate for
// changes on disk that have triggered the
// need for a restart in the first place.
const scheduler = new RunOnceScheduler(() => {
if (watcher.token.isCancellationRequested) {
return; // return early when disposed
}
// Stop/start watcher counting the restarts
this.stopWatching(watcher.request.path);
this.startWatching(watcher.request, watcher.restarts + 1);
}, delay);
scheduler.schedule();
watcher.token.onCancellationRequested(() => scheduler.dispose());
}
private stopWatching(path: string): void {
const watcher = this.watchers.get(path);
if (watcher) {
watcher.dispose();
this.watchers.delete(path);
}
}
protected normalizeRequests(requests: IWatchRequest[]): IWatchRequest[] {
const requestTrie = TernarySearchTree.forPaths<IWatchRequest>();
// Sort requests by path length to have shortest first
// to have a way to prevent children to be watched if
// parents exist.
requests.sort((requestA, requestB) => requestA.path.length - requestB.path.length);
// Only consider requests for watching that are not
// a child of an existing request path to prevent
// duplication.
//
// However, allow explicit requests to watch folders
// that are symbolic links because the NSFW watcher
// does not allow to recursively watch symbolic links.
for (const request of requests) {
if (requestTrie.findSubstr(request.path)) {
try {
const realpath = realpathSync(request.path);
if (realpath === request.path) {
this.warn(`ignoring a path for watching who's parent is already watched: ${request.path}`);
continue; // path is not a symbolic link or similar
}
} catch (error) {
continue; // invalid path - ignore from watching
}
}
requestTrie.set(request.path, request);
}
return Array.from(requestTrie).map(([, request]) => request);
}
private isPathIgnored(absolutePath: string, ignored: ParsedPattern[]): boolean {
return ignored.some(ignore => ignore(absolutePath));
}
async setVerboseLogging(enabled: boolean): Promise<void> {
this.verboseLogging = enabled;
}
private log(message: string) {
this._onDidLogMessage.fire({ type: 'trace', message: this.toMessage(message) });
}
private warn(message: string, watcher?: IWatcher) {
this._onDidLogMessage.fire({ type: 'warn', message: this.toMessage(message, watcher) });
}
private error(message: string, watcher: IWatcher | undefined) {
this._onDidLogMessage.fire({ type: 'error', message: this.toMessage(message, watcher) });
}
private debug(message: string): void {
this._onDidLogMessage.fire({ type: 'debug', message: this.toMessage(message) });
}
private toMessage(message: string, watcher?: IWatcher): string {
return watcher ? `[File Watcher (nsfw)] ${message} (path: ${watcher.request.path})` : `[File Watcher (nsfw)] ${message}`;
}
}

View file

@ -1,39 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Event } from 'vs/base/common/event';
import { IDiskFileChange, ILogMessage, IWatchRequest } from 'vs/platform/files/common/watcher';
export interface IWatcherService {
/**
* A normalized file change event from the raw events
* the watcher emits.
*/
readonly onDidChangeFile: Event<IDiskFileChange[]>;
/**
* An event to indicate a message that should get logged.
*/
readonly onDidLogMessage: Event<ILogMessage>;
/**
* Configures the watcher service to watch according
* to the requests. Any existing watched path that
* is not in the array, will be removed from watching
* and any new path will be added to watching.
*/
watch(requests: IWatchRequest[]): Promise<void>;
/**
* Enable verbose logging in the watcher.
*/
setVerboseLogging(enabled: boolean): Promise<void>;
/**
* Stop all watchers.
*/
stop(): Promise<void>;
}

View file

@ -1,74 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { FileAccess } from 'vs/base/common/network';
import { getNextTickChannel, ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
import { IWatcherService } from 'vs/platform/files/node/watcher/nsfw/watcher';
import { IDiskFileChange, ILogMessage, IWatchRequest, WatcherService } from 'vs/platform/files/common/watcher';
export class FileWatcher extends WatcherService {
private service: IWatcherService | undefined;
private isDisposed = false;
constructor(
private readonly onDidFilesChange: (changes: IDiskFileChange[]) => void,
private readonly onLogMessage: (msg: ILogMessage) => void,
private verboseLogging: boolean
) {
super();
this.startWatching();
}
private startWatching(): void {
const client = this._register(new Client(
FileAccess.asFileUri('bootstrap-fork', require).fsPath,
{
serverName: 'File Watcher (nsfw)',
args: ['--type=watcherServiceNSFW'],
env: {
VSCODE_AMD_ENTRYPOINT: 'vs/platform/files/node/watcher/nsfw/watcherApp',
VSCODE_PIPE_LOGGING: 'true',
VSCODE_VERBOSE_LOGGING: 'true' // transmit console logs from server to client
}
}
));
// Initialize watcher
this.service = ProxyChannel.toService<IWatcherService>(getNextTickChannel(client.getChannel('watcher')));
this.service.setVerboseLogging(this.verboseLogging);
// Wire in event handlers
this._register(this.service.onDidChangeFile(e => !this.isDisposed && this.onDidFilesChange(e)));
this._register(this.service.onDidLogMessage(e => this.onLogMessage(e)));
}
async setVerboseLogging(verboseLogging: boolean): Promise<void> {
this.verboseLogging = verboseLogging;
if (!this.isDisposed) {
await this.service?.setVerboseLogging(verboseLogging);
}
}
error(message: string) {
this.onLogMessage({ type: 'error', message: `[File Watcher (nsfw)] ${message}` });
}
async watch(requests: IWatchRequest[]): Promise<void> {
if (!this.isDisposed) {
await this.service?.watch(requests);
}
}
override dispose(): void {
this.isDisposed = true;
super.dispose();
}
}

View file

@ -22,9 +22,9 @@ import { generateUuid } from 'vs/base/common/uuid';
import { realcaseSync, realpathSync } from 'vs/base/node/extpath';
import { watchFolder } from 'vs/base/node/watcher';
import { FileChangeType } from 'vs/platform/files/common/files';
import { IDiskFileChange, ILogMessage, coalesceEvents, IWatchRequest, IWatcherService } from 'vs/platform/files/common/watcher';
import { IDiskFileChange, ILogMessage, coalesceEvents, IWatchRequest, IRecursiveWatcher } from 'vs/platform/files/common/watcher';
export interface IWatcher {
export interface IParcelWatcherInstance {
/**
* Signals when the watcher is ready to watch.
@ -54,7 +54,7 @@ export interface IWatcher {
stop(): Promise<void>;
}
export class ParcelWatcherService extends Disposable implements IWatcherService {
export class ParcelWatcher extends Disposable implements IRecursiveWatcher {
private static readonly MAP_PARCEL_WATCHER_ACTION_TO_FILE_CHANGE = new Map<parcelWatcher.EventType, number>(
[
@ -87,7 +87,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
private readonly _onDidError = this._register(new Emitter<string>());
readonly onDidError = this._onDidError.event;
protected readonly watchers = new Map<string, IWatcher>();
protected readonly watchers = new Map<string, IParcelWatcherInstance>();
private verboseLogging = false;
private enospcErrorLogged = false;
@ -163,7 +163,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
// level of the file watcher as much as possible.
// Refs: https://github.com/parcel-bundler/watcher/issues/64
for (const exclude of excludes) {
const isGlob = exclude.includes(ParcelWatcherService.GLOB_MARKERS.Star);
const isGlob = exclude.includes(ParcelWatcher.GLOB_MARKERS.Star);
// Glob pattern: check for typical patterns and convert
let normalizedExclude: string | undefined = undefined;
@ -171,9 +171,9 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
// Examples: **, **/**, **\**
if (
exclude === ParcelWatcherService.GLOB_MARKERS.GlobStar ||
exclude === ParcelWatcherService.GLOB_MARKERS.GlobStarPosix ||
exclude === ParcelWatcherService.GLOB_MARKERS.GlobStarWindows
exclude === ParcelWatcher.GLOB_MARKERS.GlobStar ||
exclude === ParcelWatcher.GLOB_MARKERS.GlobStarPosix ||
exclude === ParcelWatcher.GLOB_MARKERS.GlobStarWindows
) {
normalizedExclude = path;
}
@ -184,15 +184,15 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
// - **/build-folder
// - output/**
else {
const startsWithGlobStar = exclude.startsWith(ParcelWatcherService.GLOB_MARKERS.GlobStarPathStartPosix) || exclude.startsWith(ParcelWatcherService.GLOB_MARKERS.GlobStarPathStartWindows);
const endsWithGlobStar = exclude.endsWith(ParcelWatcherService.GLOB_MARKERS.GlobStarPathEndPosix) || exclude.endsWith(ParcelWatcherService.GLOB_MARKERS.GlobStarPathEndWindows);
const startsWithGlobStar = exclude.startsWith(ParcelWatcher.GLOB_MARKERS.GlobStarPathStartPosix) || exclude.startsWith(ParcelWatcher.GLOB_MARKERS.GlobStarPathStartWindows);
const endsWithGlobStar = exclude.endsWith(ParcelWatcher.GLOB_MARKERS.GlobStarPathEndPosix) || exclude.endsWith(ParcelWatcher.GLOB_MARKERS.GlobStarPathEndWindows);
if (startsWithGlobStar || endsWithGlobStar) {
if (startsWithGlobStar && endsWithGlobStar) {
normalizedExclude = exclude.substring(ParcelWatcherService.GLOB_MARKERS.GlobStarPathStartPosix.length, exclude.length - ParcelWatcherService.GLOB_MARKERS.GlobStarPathEndPosix.length);
normalizedExclude = exclude.substring(ParcelWatcher.GLOB_MARKERS.GlobStarPathStartPosix.length, exclude.length - ParcelWatcher.GLOB_MARKERS.GlobStarPathEndPosix.length);
} else if (startsWithGlobStar) {
normalizedExclude = exclude.substring(ParcelWatcherService.GLOB_MARKERS.GlobStarPathStartPosix.length);
normalizedExclude = exclude.substring(ParcelWatcher.GLOB_MARKERS.GlobStarPathStartPosix.length);
} else {
normalizedExclude = exclude.substring(0, exclude.length - ParcelWatcherService.GLOB_MARKERS.GlobStarPathEndPosix.length);
normalizedExclude = exclude.substring(0, exclude.length - ParcelWatcher.GLOB_MARKERS.GlobStarPathEndPosix.length);
}
}
@ -201,9 +201,9 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
// Examples:
// - node_modules/* (full form: **/node_modules/*/**)
if (isLinux && normalizedExclude) {
const endsWithStar = normalizedExclude?.endsWith(ParcelWatcherService.GLOB_MARKERS.StarPathEndPosix);
const endsWithStar = normalizedExclude?.endsWith(ParcelWatcher.GLOB_MARKERS.StarPathEndPosix);
if (endsWithStar) {
normalizedExclude = normalizedExclude.substring(0, normalizedExclude.length - ParcelWatcherService.GLOB_MARKERS.StarPathEndPosix.length);
normalizedExclude = normalizedExclude.substring(0, normalizedExclude.length - ParcelWatcher.GLOB_MARKERS.StarPathEndPosix.length);
}
}
}
@ -214,7 +214,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
normalizedExclude = exclude;
}
if (!normalizedExclude || normalizedExclude.includes(ParcelWatcherService.GLOB_MARKERS.Star)) {
if (!normalizedExclude || normalizedExclude.includes(ParcelWatcher.GLOB_MARKERS.Star)) {
continue; // skip for parcel (will be applied later by our glob matching)
}
@ -249,7 +249,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
const snapshotFile = join(tmpdir(), `vscode-watcher-snapshot-${generateUuid()}`);
// Remember as watcher instance
const watcher: IWatcher = {
const watcher: IParcelWatcherInstance = {
request,
ready: instance.p,
restarts,
@ -283,7 +283,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
// We already ran before, check for events since
if (counter > 1) {
const parcelEvents = await parcelWatcher.getEventsSince(realPath, snapshotFile, { ignore, backend: ParcelWatcherService.PARCEL_WATCHER_BACKEND });
const parcelEvents = await parcelWatcher.getEventsSince(realPath, snapshotFile, { ignore, backend: ParcelWatcher.PARCEL_WATCHER_BACKEND });
if (cts.token.isCancellationRequested) {
return;
@ -294,7 +294,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
}
// Store a snapshot of files to the snapshot file
await parcelWatcher.writeSnapshot(realPath, snapshotFile, { ignore, backend: ParcelWatcherService.PARCEL_WATCHER_BACKEND });
await parcelWatcher.writeSnapshot(realPath, snapshotFile, { ignore, backend: ParcelWatcher.PARCEL_WATCHER_BACKEND });
// Signal we are ready now when the first snapshot was written
if (counter === 1) {
@ -317,7 +317,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
const instance = new DeferredPromise<parcelWatcher.AsyncSubscription | undefined>();
// Remember as watcher instance
const watcher: IWatcher = {
const watcher: IParcelWatcherInstance = {
request,
ready: instance.p,
restarts,
@ -354,10 +354,10 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
// Handle & emit events
this.onParcelEvents(parcelEvents, watcher, excludePatterns, realPathDiffers, realPathLength);
}, {
backend: ParcelWatcherService.PARCEL_WATCHER_BACKEND,
backend: ParcelWatcher.PARCEL_WATCHER_BACKEND,
ignore
}).then(parcelWatcher => {
this.debug(`Started watching: '${realPath}' with backend '${ParcelWatcherService.PARCEL_WATCHER_BACKEND}' and native excludes '${ignore?.join(', ')}'`);
this.debug(`Started watching: '${realPath}' with backend '${ParcelWatcher.PARCEL_WATCHER_BACKEND}' and native excludes '${ignore?.join(', ')}'`);
instance.complete(parcelWatcher);
}).catch(error => {
@ -367,7 +367,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
});
}
private onParcelEvents(parcelEvents: parcelWatcher.Event[], watcher: IWatcher, excludes: ParsedPattern[], realPathDiffers: boolean, realPathLength: number): void {
private onParcelEvents(parcelEvents: parcelWatcher.Event[], watcher: IParcelWatcherInstance, excludes: ParsedPattern[], realPathDiffers: boolean, realPathLength: number): void {
if (parcelEvents.length === 0) {
return;
}
@ -397,7 +397,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
const events: IDiskFileChange[] = [];
for (const { path, type: parcelEventType } of parcelEvents) {
const type = ParcelWatcherService.MAP_PARCEL_WATCHER_ACTION_TO_FILE_CHANGE.get(parcelEventType)!;
const type = ParcelWatcher.MAP_PARCEL_WATCHER_ACTION_TO_FILE_CHANGE.get(parcelEventType)!;
if (this.verboseLogging) {
this.log(`${type === FileChangeType.ADDED ? '[ADDED]' : type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${path}`);
}
@ -507,7 +507,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
});
}
private onWatchedPathDeleted(watcher: IWatcher): void {
private onWatchedPathDeleted(watcher: IParcelWatcherInstance): void {
this.warn('Watcher shutdown because watched path got deleted', watcher);
const parentPath = dirname(watcher.request.path);
@ -536,7 +536,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
}
}
private onUnexpectedError(error: unknown, watcher?: IWatcher): void {
private onUnexpectedError(error: unknown, watcher?: IParcelWatcherInstance): void {
const msg = toErrorMessage(error);
// Specially handle ENOSPC errors that can happen when
@ -570,7 +570,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
this.watchers.clear();
}
protected restartWatching(watcher: IWatcher, delay = 800): void {
protected restartWatching(watcher: IParcelWatcherInstance, delay = 800): void {
// Restart watcher delayed to accomodate for
// changes on disk that have triggered the
@ -656,11 +656,11 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
this._onDidLogMessage.fire({ type: 'trace', message: this.toMessage(message) });
}
private warn(message: string, watcher?: IWatcher) {
private warn(message: string, watcher?: IParcelWatcherInstance) {
this._onDidLogMessage.fire({ type: 'warn', message: this.toMessage(message, watcher) });
}
private error(message: string, watcher: IWatcher | undefined) {
private error(message: string, watcher: IParcelWatcherInstance | undefined) {
this._onDidLogMessage.fire({ type: 'error', message: this.toMessage(message, watcher) });
}
@ -668,7 +668,7 @@ export class ParcelWatcherService extends Disposable implements IWatcherService
this._onDidLogMessage.fire({ type: 'debug', message: this.toMessage(message) });
}
private toMessage(message: string, watcher?: IWatcher): string {
private toMessage(message: string, watcher?: IParcelWatcherInstance): string {
return watcher ? `[File Watcher (parcel)] ${message} (path: ${watcher.request.path})` : `[File Watcher (parcel)] ${message}`;
}
}

View file

@ -7,9 +7,9 @@ import { DisposableStore } from 'vs/base/common/lifecycle';
import { FileAccess } from 'vs/base/common/network';
import { getNextTickChannel, ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { Client } from 'vs/base/parts/ipc/node/ipc.cp';
import { AbstractWatcherService, IDiskFileChange, ILogMessage, IWatcherService } from 'vs/platform/files/common/watcher';
import { AbstractRecursiveWatcherClient, IDiskFileChange, ILogMessage, IRecursiveWatcher } from 'vs/platform/files/common/watcher';
export class FileWatcher extends AbstractWatcherService {
export class ParcelWatcherClient extends AbstractRecursiveWatcherClient {
constructor(
onFileChanges: (changes: IDiskFileChange[]) => void,
@ -21,7 +21,7 @@ export class FileWatcher extends AbstractWatcherService {
this.init();
}
protected override createService(disposables: DisposableStore): IWatcherService {
protected override createWatcher(disposables: DisposableStore): IRecursiveWatcher {
// Fork the parcel file watcher and build a client around
// its server for passing over requests and receiving events.
@ -29,9 +29,9 @@ export class FileWatcher extends AbstractWatcherService {
FileAccess.asFileUri('bootstrap-fork', require).fsPath,
{
serverName: 'File Watcher (parcel, node.js)',
args: ['--type=watcherServiceParcel'],
args: ['--type=parcelWatcher'],
env: {
VSCODE_AMD_ENTRYPOINT: 'vs/platform/files/node/watcher/parcel/watcherApp',
VSCODE_AMD_ENTRYPOINT: 'vs/platform/files/node/watcher/parcel/parcelWatcherMain',
VSCODE_PIPE_LOGGING: 'true',
VSCODE_VERBOSE_LOGGING: 'true' // transmit console logs from server to client
}
@ -41,6 +41,6 @@ export class FileWatcher extends AbstractWatcherService {
// React on unexpected termination of the watcher process
disposables.add(client.onDidProcessExit(({ code, signal }) => this.onError(`terminated by itself with code ${code}, signal: ${signal}`)));
return ProxyChannel.toService<IWatcherService>(getNextTickChannel(client.getChannel('watcher')));
return ProxyChannel.toService<IRecursiveWatcher>(getNextTickChannel(client.getChannel('watcher')));
}
}

View file

@ -5,8 +5,8 @@
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { Server } from 'vs/base/parts/ipc/node/ipc.cp';
import { NsfwWatcherService } from 'vs/platform/files/node/watcher/nsfw/nsfwWatcherService';
import { ParcelWatcher } from 'vs/platform/files/node/watcher/parcel/parcelWatcher';
const server = new Server('watcher');
const service = new NsfwWatcherService();
const service = new ParcelWatcher();
server.registerChannel('watcher', ProxyChannel.fromService(service));

View file

@ -1,12 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { Server } from 'vs/base/parts/ipc/node/ipc.cp';
import { ParcelWatcherService } from 'vs/platform/files/node/watcher/parcel/parcelWatcherService';
const server = new Server('watcher');
const service = new ParcelWatcherService();
server.registerChannel('watcher', ProxyChannel.fromService(service));

View file

@ -12,12 +12,12 @@ import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform';
import { Promises, RimRafMode } from 'vs/base/node/pfs';
import { flakySuite, getPathFromAmdModule, getRandomTestPath } from 'vs/base/test/node/testUtils';
import { FileChangeType } from 'vs/platform/files/common/files';
import { IWatcher, ParcelWatcherService } from 'vs/platform/files/node/watcher/parcel/parcelWatcherService';
import { IParcelWatcherInstance, ParcelWatcher } from 'vs/platform/files/node/watcher/parcel/parcelWatcher';
import { IWatchRequest } from 'vs/platform/files/common/watcher';
flakySuite('Recursive Watcher (parcel)', () => {
class TestParcelWatcherService extends ParcelWatcherService {
class TestParcelWatcher extends ParcelWatcher {
testNormalizePaths(paths: string[]): string[] {
@ -44,33 +44,33 @@ flakySuite('Recursive Watcher (parcel)', () => {
return super.toExcludePaths(path, excludes);
}
override restartWatching(watcher: IWatcher, delay = 10): void {
override restartWatching(watcher: IParcelWatcherInstance, delay = 10): void {
return super.restartWatching(watcher, delay);
}
}
let testDir: string;
let service: TestParcelWatcherService;
let watcher: TestParcelWatcher;
let loggingEnabled = false;
function enableLogging(enable: boolean) {
loggingEnabled = enable;
service?.setVerboseLogging(enable);
watcher?.setVerboseLogging(enable);
}
enableLogging(false);
setup(async () => {
service = new TestParcelWatcherService();
watcher = new TestParcelWatcher();
service.onDidLogMessage(e => {
watcher.onDidLogMessage(e => {
if (loggingEnabled) {
console.log(`[recursive watcher test message] ${e.message}`);
}
});
service.onDidError(e => {
watcher.onDidError(e => {
if (loggingEnabled) {
console.log(`[recursive watcher test error] ${e}`);
}
@ -84,7 +84,7 @@ flakySuite('Recursive Watcher (parcel)', () => {
});
teardown(async () => {
await service.stop();
await watcher.stop();
// Possible that the file watcher is still holding
// onto the folders on Windows specifically and the
@ -101,7 +101,7 @@ flakySuite('Recursive Watcher (parcel)', () => {
}
}
async function awaitEvent(service: TestParcelWatcherService, path: string, type: FileChangeType, failOnEventReason?: string): Promise<void> {
async function awaitEvent(service: TestParcelWatcher, path: string, type: FileChangeType, failOnEventReason?: string): Promise<void> {
if (loggingEnabled) {
console.log(`Awaiting change type '${toMsg(type)}' on file '${path}'`);
}
@ -130,7 +130,7 @@ flakySuite('Recursive Watcher (parcel)', () => {
await timeout(1);
}
function awaitMessage(service: TestParcelWatcherService, type: 'trace' | 'warn' | 'error' | 'info' | 'debug'): Promise<void> {
function awaitMessage(service: TestParcelWatcher, type: 'trace' | 'warn' | 'error' | 'info' | 'debug'): Promise<void> {
if (loggingEnabled) {
console.log(`Awaiting message of type ${type}`);
}
@ -147,25 +147,25 @@ flakySuite('Recursive Watcher (parcel)', () => {
}
test('basics', async function () {
await service.watch([{ path: testDir, excludes: [] }]);
await watcher.watch([{ path: testDir, excludes: [] }]);
// New file
const newFilePath = join(testDir, 'deep', 'newFile.txt');
let changeFuture: Promise<unknown> = awaitEvent(service, newFilePath, FileChangeType.ADDED);
let changeFuture: Promise<unknown> = awaitEvent(watcher, newFilePath, FileChangeType.ADDED);
await Promises.writeFile(newFilePath, 'Hello World');
await changeFuture;
// New folder
const newFolderPath = join(testDir, 'deep', 'New Folder');
changeFuture = awaitEvent(service, newFolderPath, FileChangeType.ADDED);
changeFuture = awaitEvent(watcher, newFolderPath, FileChangeType.ADDED);
await Promises.mkdir(newFolderPath);
await changeFuture;
// Rename file
let renamedFilePath = join(testDir, 'deep', 'renamedFile.txt');
changeFuture = Promise.all([
awaitEvent(service, newFilePath, FileChangeType.DELETED),
awaitEvent(service, renamedFilePath, FileChangeType.ADDED)
awaitEvent(watcher, newFilePath, FileChangeType.DELETED),
awaitEvent(watcher, renamedFilePath, FileChangeType.ADDED)
]);
await Promises.rename(newFilePath, renamedFilePath);
await changeFuture;
@ -173,8 +173,8 @@ flakySuite('Recursive Watcher (parcel)', () => {
// Rename folder
let renamedFolderPath = join(testDir, 'deep', 'Renamed Folder');
changeFuture = Promise.all([
awaitEvent(service, newFolderPath, FileChangeType.DELETED),
awaitEvent(service, renamedFolderPath, FileChangeType.ADDED)
awaitEvent(watcher, newFolderPath, FileChangeType.DELETED),
awaitEvent(watcher, renamedFolderPath, FileChangeType.ADDED)
]);
await Promises.rename(newFolderPath, renamedFolderPath);
await changeFuture;
@ -182,8 +182,8 @@ flakySuite('Recursive Watcher (parcel)', () => {
// Rename file (same name, different case)
const caseRenamedFilePath = join(testDir, 'deep', 'RenamedFile.txt');
changeFuture = Promise.all([
awaitEvent(service, renamedFilePath, FileChangeType.DELETED),
awaitEvent(service, caseRenamedFilePath, FileChangeType.ADDED)
awaitEvent(watcher, renamedFilePath, FileChangeType.DELETED),
awaitEvent(watcher, caseRenamedFilePath, FileChangeType.ADDED)
]);
await Promises.rename(renamedFilePath, caseRenamedFilePath);
await changeFuture;
@ -192,8 +192,8 @@ flakySuite('Recursive Watcher (parcel)', () => {
// Rename folder (same name, different case)
const caseRenamedFolderPath = join(testDir, 'deep', 'REnamed Folder');
changeFuture = Promise.all([
awaitEvent(service, renamedFolderPath, FileChangeType.DELETED),
awaitEvent(service, caseRenamedFolderPath, FileChangeType.ADDED)
awaitEvent(watcher, renamedFolderPath, FileChangeType.DELETED),
awaitEvent(watcher, caseRenamedFolderPath, FileChangeType.ADDED)
]);
await Promises.rename(renamedFolderPath, caseRenamedFolderPath);
await changeFuture;
@ -202,8 +202,8 @@ flakySuite('Recursive Watcher (parcel)', () => {
// Move file
const movedFilepath = join(testDir, 'movedFile.txt');
changeFuture = Promise.all([
awaitEvent(service, renamedFilePath, FileChangeType.DELETED),
awaitEvent(service, movedFilepath, FileChangeType.ADDED)
awaitEvent(watcher, renamedFilePath, FileChangeType.DELETED),
awaitEvent(watcher, movedFilepath, FileChangeType.ADDED)
]);
await Promises.rename(renamedFilePath, movedFilepath);
await changeFuture;
@ -211,32 +211,32 @@ flakySuite('Recursive Watcher (parcel)', () => {
// Move folder
const movedFolderpath = join(testDir, 'Moved Folder');
changeFuture = Promise.all([
awaitEvent(service, renamedFolderPath, FileChangeType.DELETED),
awaitEvent(service, movedFolderpath, FileChangeType.ADDED)
awaitEvent(watcher, renamedFolderPath, FileChangeType.DELETED),
awaitEvent(watcher, movedFolderpath, FileChangeType.ADDED)
]);
await Promises.rename(renamedFolderPath, movedFolderpath);
await changeFuture;
// Copy file
const copiedFilepath = join(testDir, 'deep', 'copiedFile.txt');
changeFuture = awaitEvent(service, copiedFilepath, FileChangeType.ADDED);
changeFuture = awaitEvent(watcher, copiedFilepath, FileChangeType.ADDED);
await Promises.copyFile(movedFilepath, copiedFilepath);
await changeFuture;
// Copy folder
const copiedFolderpath = join(testDir, 'deep', 'Copied Folder');
changeFuture = awaitEvent(service, copiedFolderpath, FileChangeType.ADDED);
changeFuture = awaitEvent(watcher, copiedFolderpath, FileChangeType.ADDED);
await Promises.copy(movedFolderpath, copiedFolderpath, { preserveSymlinks: false });
await changeFuture;
// Change file
changeFuture = awaitEvent(service, copiedFilepath, FileChangeType.UPDATED);
changeFuture = awaitEvent(watcher, copiedFilepath, FileChangeType.UPDATED);
await Promises.writeFile(copiedFilepath, 'Hello Change');
await changeFuture;
// Create new file
const anotherNewFilePath = join(testDir, 'deep', 'anotherNewFile.txt');
changeFuture = awaitEvent(service, anotherNewFilePath, FileChangeType.ADDED);
changeFuture = awaitEvent(watcher, anotherNewFilePath, FileChangeType.ADDED);
await Promises.writeFile(anotherNewFilePath, 'Hello Another World');
await changeFuture;
@ -246,65 +246,65 @@ flakySuite('Recursive Watcher (parcel)', () => {
if (!isMacintosh) {
// Read file does not emit event
changeFuture = awaitEvent(service, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-read-file');
changeFuture = awaitEvent(watcher, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-read-file');
await Promises.readFile(anotherNewFilePath);
await Promise.race([timeout(100), changeFuture]);
// Stat file does not emit event
changeFuture = awaitEvent(service, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
changeFuture = awaitEvent(watcher, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
await Promises.stat(anotherNewFilePath);
await Promise.race([timeout(100), changeFuture]);
// Stat folder does not emit event
changeFuture = awaitEvent(service, copiedFolderpath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
changeFuture = awaitEvent(watcher, copiedFolderpath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
await Promises.stat(copiedFolderpath);
await Promise.race([timeout(100), changeFuture]);
}
// Delete file
changeFuture = awaitEvent(service, copiedFilepath, FileChangeType.DELETED);
changeFuture = awaitEvent(watcher, copiedFilepath, FileChangeType.DELETED);
await Promises.unlink(copiedFilepath);
await changeFuture;
// Delete folder
changeFuture = awaitEvent(service, copiedFolderpath, FileChangeType.DELETED);
changeFuture = awaitEvent(watcher, copiedFolderpath, FileChangeType.DELETED);
await Promises.rmdir(copiedFolderpath);
await changeFuture;
});
(isMacintosh /* this test seems not possible with fsevents backend */ ? test.skip : test)('basics (atomic writes)', async function () {
await service.watch([{ path: testDir, excludes: [] }]);
await watcher.watch([{ path: testDir, excludes: [] }]);
// Delete + Recreate file
const newFilePath = join(testDir, 'deep', 'conway.js');
let changeFuture: Promise<unknown> = awaitEvent(service, newFilePath, FileChangeType.UPDATED);
let changeFuture: Promise<unknown> = awaitEvent(watcher, newFilePath, FileChangeType.UPDATED);
await Promises.unlink(newFilePath);
Promises.writeFile(newFilePath, 'Hello Atomic World');
await changeFuture;
});
(!isLinux /* polling is only used in linux environments (WSL) */ ? test.skip : test)('basics (polling)', async function () {
await service.watch([{ path: testDir, excludes: [], pollingInterval: 100 }]);
await watcher.watch([{ path: testDir, excludes: [], pollingInterval: 100 }]);
// New file
const newFilePath = join(testDir, 'deep', 'newFile.txt');
let changeFuture: Promise<unknown> = awaitEvent(service, newFilePath, FileChangeType.ADDED);
let changeFuture: Promise<unknown> = awaitEvent(watcher, newFilePath, FileChangeType.ADDED);
await Promises.writeFile(newFilePath, 'Hello World');
await changeFuture;
// Change file
changeFuture = awaitEvent(service, newFilePath, FileChangeType.UPDATED);
changeFuture = awaitEvent(watcher, newFilePath, FileChangeType.UPDATED);
await Promises.writeFile(newFilePath, 'Hello Change');
await changeFuture;
// Delete file
changeFuture = awaitEvent(service, newFilePath, FileChangeType.DELETED);
changeFuture = awaitEvent(watcher, newFilePath, FileChangeType.DELETED);
await Promises.unlink(newFilePath);
await changeFuture;
});
test('multiple events', async function () {
await service.watch([{ path: testDir, excludes: [] }]);
await watcher.watch([{ path: testDir, excludes: [] }]);
await Promises.mkdir(join(testDir, 'deep-multiple'));
// multiple add
@ -316,12 +316,12 @@ flakySuite('Recursive Watcher (parcel)', () => {
const newFilePath5 = join(testDir, 'deep-multiple', 'newFile-2.txt');
const newFilePath6 = join(testDir, 'deep-multiple', 'newFile-3.txt');
const addedFuture1: Promise<unknown> = awaitEvent(service, newFilePath1, FileChangeType.ADDED);
const addedFuture2: Promise<unknown> = awaitEvent(service, newFilePath2, FileChangeType.ADDED);
const addedFuture3: Promise<unknown> = awaitEvent(service, newFilePath3, FileChangeType.ADDED);
const addedFuture4: Promise<unknown> = awaitEvent(service, newFilePath4, FileChangeType.ADDED);
const addedFuture5: Promise<unknown> = awaitEvent(service, newFilePath5, FileChangeType.ADDED);
const addedFuture6: Promise<unknown> = awaitEvent(service, newFilePath6, FileChangeType.ADDED);
const addedFuture1: Promise<unknown> = awaitEvent(watcher, newFilePath1, FileChangeType.ADDED);
const addedFuture2: Promise<unknown> = awaitEvent(watcher, newFilePath2, FileChangeType.ADDED);
const addedFuture3: Promise<unknown> = awaitEvent(watcher, newFilePath3, FileChangeType.ADDED);
const addedFuture4: Promise<unknown> = awaitEvent(watcher, newFilePath4, FileChangeType.ADDED);
const addedFuture5: Promise<unknown> = awaitEvent(watcher, newFilePath5, FileChangeType.ADDED);
const addedFuture6: Promise<unknown> = awaitEvent(watcher, newFilePath6, FileChangeType.ADDED);
await Promise.all([
await Promises.writeFile(newFilePath1, 'Hello World 1'),
@ -336,12 +336,12 @@ flakySuite('Recursive Watcher (parcel)', () => {
// multiple change
const changeFuture1: Promise<unknown> = awaitEvent(service, newFilePath1, FileChangeType.UPDATED);
const changeFuture2: Promise<unknown> = awaitEvent(service, newFilePath2, FileChangeType.UPDATED);
const changeFuture3: Promise<unknown> = awaitEvent(service, newFilePath3, FileChangeType.UPDATED);
const changeFuture4: Promise<unknown> = awaitEvent(service, newFilePath4, FileChangeType.UPDATED);
const changeFuture5: Promise<unknown> = awaitEvent(service, newFilePath5, FileChangeType.UPDATED);
const changeFuture6: Promise<unknown> = awaitEvent(service, newFilePath6, FileChangeType.UPDATED);
const changeFuture1: Promise<unknown> = awaitEvent(watcher, newFilePath1, FileChangeType.UPDATED);
const changeFuture2: Promise<unknown> = awaitEvent(watcher, newFilePath2, FileChangeType.UPDATED);
const changeFuture3: Promise<unknown> = awaitEvent(watcher, newFilePath3, FileChangeType.UPDATED);
const changeFuture4: Promise<unknown> = awaitEvent(watcher, newFilePath4, FileChangeType.UPDATED);
const changeFuture5: Promise<unknown> = awaitEvent(watcher, newFilePath5, FileChangeType.UPDATED);
const changeFuture6: Promise<unknown> = awaitEvent(watcher, newFilePath6, FileChangeType.UPDATED);
await Promise.all([
await Promises.writeFile(newFilePath1, 'Hello Update 1'),
@ -356,10 +356,10 @@ flakySuite('Recursive Watcher (parcel)', () => {
// copy with multiple files
const copyFuture1: Promise<unknown> = awaitEvent(service, join(testDir, 'deep-multiple-copy', 'newFile-1.txt'), FileChangeType.ADDED);
const copyFuture2: Promise<unknown> = awaitEvent(service, join(testDir, 'deep-multiple-copy', 'newFile-2.txt'), FileChangeType.ADDED);
const copyFuture3: Promise<unknown> = awaitEvent(service, join(testDir, 'deep-multiple-copy', 'newFile-3.txt'), FileChangeType.ADDED);
const copyFuture4: Promise<unknown> = awaitEvent(service, join(testDir, 'deep-multiple-copy'), FileChangeType.ADDED);
const copyFuture1: Promise<unknown> = awaitEvent(watcher, join(testDir, 'deep-multiple-copy', 'newFile-1.txt'), FileChangeType.ADDED);
const copyFuture2: Promise<unknown> = awaitEvent(watcher, join(testDir, 'deep-multiple-copy', 'newFile-2.txt'), FileChangeType.ADDED);
const copyFuture3: Promise<unknown> = awaitEvent(watcher, join(testDir, 'deep-multiple-copy', 'newFile-3.txt'), FileChangeType.ADDED);
const copyFuture4: Promise<unknown> = awaitEvent(watcher, join(testDir, 'deep-multiple-copy'), FileChangeType.ADDED);
await Promises.copy(join(testDir, 'deep-multiple'), join(testDir, 'deep-multiple-copy'), { preserveSymlinks: false });
@ -367,12 +367,12 @@ flakySuite('Recursive Watcher (parcel)', () => {
// multiple delete (single files)
const deleteFuture1: Promise<unknown> = awaitEvent(service, newFilePath1, FileChangeType.DELETED);
const deleteFuture2: Promise<unknown> = awaitEvent(service, newFilePath2, FileChangeType.DELETED);
const deleteFuture3: Promise<unknown> = awaitEvent(service, newFilePath3, FileChangeType.DELETED);
const deleteFuture4: Promise<unknown> = awaitEvent(service, newFilePath4, FileChangeType.DELETED);
const deleteFuture5: Promise<unknown> = awaitEvent(service, newFilePath5, FileChangeType.DELETED);
const deleteFuture6: Promise<unknown> = awaitEvent(service, newFilePath6, FileChangeType.DELETED);
const deleteFuture1: Promise<unknown> = awaitEvent(watcher, newFilePath1, FileChangeType.DELETED);
const deleteFuture2: Promise<unknown> = awaitEvent(watcher, newFilePath2, FileChangeType.DELETED);
const deleteFuture3: Promise<unknown> = awaitEvent(watcher, newFilePath3, FileChangeType.DELETED);
const deleteFuture4: Promise<unknown> = awaitEvent(watcher, newFilePath4, FileChangeType.DELETED);
const deleteFuture5: Promise<unknown> = awaitEvent(watcher, newFilePath5, FileChangeType.DELETED);
const deleteFuture6: Promise<unknown> = awaitEvent(watcher, newFilePath6, FileChangeType.DELETED);
await Promise.all([
await Promises.unlink(newFilePath1),
@ -387,8 +387,8 @@ flakySuite('Recursive Watcher (parcel)', () => {
// multiple delete (folder)
const deleteFolderFuture1: Promise<unknown> = awaitEvent(service, join(testDir, 'deep-multiple'), FileChangeType.DELETED);
const deleteFolderFuture2: Promise<unknown> = awaitEvent(service, join(testDir, 'deep-multiple-copy'), FileChangeType.DELETED);
const deleteFolderFuture1: Promise<unknown> = awaitEvent(watcher, join(testDir, 'deep-multiple'), FileChangeType.DELETED);
const deleteFolderFuture2: Promise<unknown> = awaitEvent(watcher, join(testDir, 'deep-multiple-copy'), FileChangeType.DELETED);
await Promise.all([Promises.rm(join(testDir, 'deep-multiple'), RimRafMode.UNLINK), Promises.rm(join(testDir, 'deep-multiple-copy'), RimRafMode.UNLINK)]);
@ -396,35 +396,35 @@ flakySuite('Recursive Watcher (parcel)', () => {
});
test('subsequent watch updates watchers (path)', async function () {
await service.watch([{ path: testDir, excludes: [join(realpathSync(testDir), 'unrelated')] }]);
await watcher.watch([{ path: testDir, excludes: [join(realpathSync(testDir), 'unrelated')] }]);
// New file (*.txt)
let newTextFilePath = join(testDir, 'deep', 'newFile.txt');
let changeFuture: Promise<unknown> = awaitEvent(service, newTextFilePath, FileChangeType.ADDED);
let changeFuture: Promise<unknown> = awaitEvent(watcher, newTextFilePath, FileChangeType.ADDED);
await Promises.writeFile(newTextFilePath, 'Hello World');
await changeFuture;
await service.watch([{ path: join(testDir, 'deep'), excludes: [join(realpathSync(testDir), 'unrelated')] }]);
await watcher.watch([{ path: join(testDir, 'deep'), excludes: [join(realpathSync(testDir), 'unrelated')] }]);
newTextFilePath = join(testDir, 'deep', 'newFile2.txt');
changeFuture = awaitEvent(service, newTextFilePath, FileChangeType.ADDED);
changeFuture = awaitEvent(watcher, newTextFilePath, FileChangeType.ADDED);
await Promises.writeFile(newTextFilePath, 'Hello World');
await changeFuture;
await service.watch([{ path: join(testDir, 'deep'), excludes: [realpathSync(testDir)] }]);
await service.watch([{ path: join(testDir, 'deep'), excludes: [] }]);
await watcher.watch([{ path: join(testDir, 'deep'), excludes: [realpathSync(testDir)] }]);
await watcher.watch([{ path: join(testDir, 'deep'), excludes: [] }]);
newTextFilePath = join(testDir, 'deep', 'newFile3.txt');
changeFuture = awaitEvent(service, newTextFilePath, FileChangeType.ADDED);
changeFuture = awaitEvent(watcher, newTextFilePath, FileChangeType.ADDED);
await Promises.writeFile(newTextFilePath, 'Hello World');
await changeFuture;
});
test('subsequent watch updates watchers (excludes)', async function () {
await service.watch([{ path: testDir, excludes: [realpathSync(testDir)] }]);
await service.watch([{ path: testDir, excludes: [] }]);
await watcher.watch([{ path: testDir, excludes: [realpathSync(testDir)] }]);
await watcher.watch([{ path: testDir, excludes: [] }]);
// New file (*.txt)
let newTextFilePath = join(testDir, 'deep', 'newFile.txt');
let changeFuture: Promise<unknown> = awaitEvent(service, newTextFilePath, FileChangeType.ADDED);
let changeFuture: Promise<unknown> = awaitEvent(watcher, newTextFilePath, FileChangeType.ADDED);
await Promises.writeFile(newTextFilePath, 'Hello World');
await changeFuture;
});
@ -434,11 +434,11 @@ flakySuite('Recursive Watcher (parcel)', () => {
const linkTarget = join(testDir, 'deep');
await Promises.symlink(linkTarget, link);
await service.watch([{ path: link, excludes: [] }]);
await watcher.watch([{ path: link, excludes: [] }]);
// New file
const newFilePath = join(link, 'newFile.txt');
let changeFuture: Promise<unknown> = awaitEvent(service, newFilePath, FileChangeType.ADDED);
let changeFuture: Promise<unknown> = awaitEvent(watcher, newFilePath, FileChangeType.ADDED);
await Promises.writeFile(newFilePath, 'Hello World');
await changeFuture;
});
@ -448,11 +448,11 @@ flakySuite('Recursive Watcher (parcel)', () => {
const linkTarget = join(testDir, 'deep');
await Promises.symlink(linkTarget, link);
await service.watch([{ path: testDir, excludes: [] }, { path: link, excludes: [] }]);
await watcher.watch([{ path: testDir, excludes: [] }, { path: link, excludes: [] }]);
// New file
const newFilePath = join(link, 'newFile.txt');
let changeFuture: Promise<unknown> = awaitEvent(service, newFilePath, FileChangeType.ADDED);
let changeFuture: Promise<unknown> = awaitEvent(watcher, newFilePath, FileChangeType.ADDED);
await Promises.writeFile(newFilePath, 'Hello World');
await changeFuture;
});
@ -460,11 +460,11 @@ flakySuite('Recursive Watcher (parcel)', () => {
(isLinux /* linux: is case sensitive */ ? test.skip : test)('wrong casing', async function () {
const deepWrongCasedPath = join(testDir, 'DEEP');
await service.watch([{ path: deepWrongCasedPath, excludes: [] }]);
await watcher.watch([{ path: deepWrongCasedPath, excludes: [] }]);
// New file
const newFilePath = join(deepWrongCasedPath, 'newFile.txt');
let changeFuture: Promise<unknown> = awaitEvent(service, newFilePath, FileChangeType.ADDED);
let changeFuture: Promise<unknown> = awaitEvent(watcher, newFilePath, FileChangeType.ADDED);
await Promises.writeFile(newFilePath, 'Hello World');
await changeFuture;
});
@ -472,54 +472,54 @@ flakySuite('Recursive Watcher (parcel)', () => {
test('invalid folder does not explode', async function () {
const invalidPath = join(testDir, 'invalid');
await service.watch([{ path: invalidPath, excludes: [] }]);
await watcher.watch([{ path: invalidPath, excludes: [] }]);
});
test('deleting watched path is handled properly', async function () {
const watchedPath = join(testDir, 'deep');
await service.watch([{ path: watchedPath, excludes: [] }]);
await watcher.watch([{ path: watchedPath, excludes: [] }]);
// Delete watched path and await
const warnFuture = awaitMessage(service, 'warn');
const warnFuture = awaitMessage(watcher, 'warn');
await Promises.rm(watchedPath, RimRafMode.UNLINK);
await warnFuture;
// Restore watched path
await Promises.mkdir(watchedPath);
await timeout(1500); // restart is delayed
await service.whenReady();
await watcher.whenReady();
// Verify events come in again
const newFilePath = join(watchedPath, 'newFile.txt');
const changeFuture = awaitEvent(service, newFilePath, FileChangeType.ADDED);
const changeFuture = awaitEvent(watcher, newFilePath, FileChangeType.ADDED);
await Promises.writeFile(newFilePath, 'Hello World');
await changeFuture;
});
test('should not exclude roots that do not overlap', () => {
if (isWindows) {
assert.deepStrictEqual(service.testNormalizePaths(['C:\\a']), ['C:\\a']);
assert.deepStrictEqual(service.testNormalizePaths(['C:\\a', 'C:\\b']), ['C:\\a', 'C:\\b']);
assert.deepStrictEqual(service.testNormalizePaths(['C:\\a', 'C:\\b', 'C:\\c\\d\\e']), ['C:\\a', 'C:\\b', 'C:\\c\\d\\e']);
assert.deepStrictEqual(watcher.testNormalizePaths(['C:\\a']), ['C:\\a']);
assert.deepStrictEqual(watcher.testNormalizePaths(['C:\\a', 'C:\\b']), ['C:\\a', 'C:\\b']);
assert.deepStrictEqual(watcher.testNormalizePaths(['C:\\a', 'C:\\b', 'C:\\c\\d\\e']), ['C:\\a', 'C:\\b', 'C:\\c\\d\\e']);
} else {
assert.deepStrictEqual(service.testNormalizePaths(['/a']), ['/a']);
assert.deepStrictEqual(service.testNormalizePaths(['/a', '/b']), ['/a', '/b']);
assert.deepStrictEqual(service.testNormalizePaths(['/a', '/b', '/c/d/e']), ['/a', '/b', '/c/d/e']);
assert.deepStrictEqual(watcher.testNormalizePaths(['/a']), ['/a']);
assert.deepStrictEqual(watcher.testNormalizePaths(['/a', '/b']), ['/a', '/b']);
assert.deepStrictEqual(watcher.testNormalizePaths(['/a', '/b', '/c/d/e']), ['/a', '/b', '/c/d/e']);
}
});
test('should remove sub-folders of other paths', () => {
if (isWindows) {
assert.deepStrictEqual(service.testNormalizePaths(['C:\\a', 'C:\\a\\b']), ['C:\\a']);
assert.deepStrictEqual(service.testNormalizePaths(['C:\\a', 'C:\\b', 'C:\\a\\b']), ['C:\\a', 'C:\\b']);
assert.deepStrictEqual(service.testNormalizePaths(['C:\\b\\a', 'C:\\a', 'C:\\b', 'C:\\a\\b']), ['C:\\a', 'C:\\b']);
assert.deepStrictEqual(service.testNormalizePaths(['C:\\a', 'C:\\a\\b', 'C:\\a\\c\\d']), ['C:\\a']);
assert.deepStrictEqual(watcher.testNormalizePaths(['C:\\a', 'C:\\a\\b']), ['C:\\a']);
assert.deepStrictEqual(watcher.testNormalizePaths(['C:\\a', 'C:\\b', 'C:\\a\\b']), ['C:\\a', 'C:\\b']);
assert.deepStrictEqual(watcher.testNormalizePaths(['C:\\b\\a', 'C:\\a', 'C:\\b', 'C:\\a\\b']), ['C:\\a', 'C:\\b']);
assert.deepStrictEqual(watcher.testNormalizePaths(['C:\\a', 'C:\\a\\b', 'C:\\a\\c\\d']), ['C:\\a']);
} else {
assert.deepStrictEqual(service.testNormalizePaths(['/a', '/a/b']), ['/a']);
assert.deepStrictEqual(service.testNormalizePaths(['/a', '/b', '/a/b']), ['/a', '/b']);
assert.deepStrictEqual(service.testNormalizePaths(['/b/a', '/a', '/b', '/a/b']), ['/a', '/b']);
assert.deepStrictEqual(service.testNormalizePaths(['/a', '/a/b', '/a/c/d']), ['/a']);
assert.deepStrictEqual(watcher.testNormalizePaths(['/a', '/a/b']), ['/a']);
assert.deepStrictEqual(watcher.testNormalizePaths(['/a', '/b', '/a/b']), ['/a', '/b']);
assert.deepStrictEqual(watcher.testNormalizePaths(['/b/a', '/a', '/b', '/a/b']), ['/a', '/b']);
assert.deepStrictEqual(watcher.testNormalizePaths(['/a', '/a/b', '/a/c/d']), ['/a']);
}
});
@ -527,16 +527,16 @@ flakySuite('Recursive Watcher (parcel)', () => {
// undefined / empty
assert.strictEqual(service.toExcludePaths(testDir, undefined), undefined);
assert.strictEqual(service.toExcludePaths(testDir, []), undefined);
assert.strictEqual(watcher.toExcludePaths(testDir, undefined), undefined);
assert.strictEqual(watcher.toExcludePaths(testDir, []), undefined);
// absolute paths
let excludes = service.toExcludePaths(testDir, [testDir]);
let excludes = watcher.toExcludePaths(testDir, [testDir]);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], testDir);
excludes = service.toExcludePaths(testDir, [`${testDir}${sep}`, join(testDir, 'foo', 'bar'), `${join(testDir, 'other', 'deep')}${sep}`]);
excludes = watcher.toExcludePaths(testDir, [`${testDir}${sep}`, join(testDir, 'foo', 'bar'), `${join(testDir, 'other', 'deep')}${sep}`]);
assert.strictEqual(excludes?.length, 3);
assert.strictEqual(excludes[0], testDir);
assert.strictEqual(excludes[1], join(testDir, 'foo', 'bar'));
@ -544,22 +544,22 @@ flakySuite('Recursive Watcher (parcel)', () => {
// wrong casing is normalized for root
if (!isLinux) {
excludes = service.toExcludePaths(testDir, [join(testDir.toUpperCase(), 'node_modules', '**')]);
excludes = watcher.toExcludePaths(testDir, [join(testDir.toUpperCase(), 'node_modules', '**')]);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], join(testDir, 'node_modules'));
}
// exclude ignored if not parent of watched dir
excludes = service.toExcludePaths(testDir, [join(dirname(testDir), 'node_modules', '**')]);
excludes = watcher.toExcludePaths(testDir, [join(dirname(testDir), 'node_modules', '**')]);
assert.strictEqual(excludes, undefined);
// relative paths
excludes = service.toExcludePaths(testDir, ['.']);
excludes = watcher.toExcludePaths(testDir, ['.']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], testDir);
excludes = service.toExcludePaths(testDir, ['foo', `bar${sep}`, join('foo', 'bar'), `${join('other', 'deep')}${sep}`]);
excludes = watcher.toExcludePaths(testDir, ['foo', `bar${sep}`, join('foo', 'bar'), `${join('other', 'deep')}${sep}`]);
assert.strictEqual(excludes?.length, 4);
assert.strictEqual(excludes[0], join(testDir, 'foo'));
assert.strictEqual(excludes[1], join(testDir, 'bar'));
@ -568,51 +568,51 @@ flakySuite('Recursive Watcher (parcel)', () => {
// simple globs (relative)
excludes = service.toExcludePaths(testDir, ['**']);
excludes = watcher.toExcludePaths(testDir, ['**']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], testDir);
excludes = service.toExcludePaths(testDir, ['**/**']);
excludes = watcher.toExcludePaths(testDir, ['**/**']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], testDir);
excludes = service.toExcludePaths(testDir, ['**\\**']);
excludes = watcher.toExcludePaths(testDir, ['**\\**']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], testDir);
excludes = service.toExcludePaths(testDir, ['**/node_modules/**']);
excludes = watcher.toExcludePaths(testDir, ['**/node_modules/**']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], join(testDir, 'node_modules'));
excludes = service.toExcludePaths(testDir, ['**/.git/objects/**']);
excludes = watcher.toExcludePaths(testDir, ['**/.git/objects/**']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], join(testDir, '.git', 'objects'));
excludes = service.toExcludePaths(testDir, ['**/node_modules']);
excludes = watcher.toExcludePaths(testDir, ['**/node_modules']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], join(testDir, 'node_modules'));
excludes = service.toExcludePaths(testDir, ['**/.git/objects']);
excludes = watcher.toExcludePaths(testDir, ['**/.git/objects']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], join(testDir, '.git', 'objects'));
excludes = service.toExcludePaths(testDir, ['node_modules/**']);
excludes = watcher.toExcludePaths(testDir, ['node_modules/**']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], join(testDir, 'node_modules'));
excludes = service.toExcludePaths(testDir, ['.git/objects/**']);
excludes = watcher.toExcludePaths(testDir, ['.git/objects/**']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], join(testDir, '.git', 'objects'));
// simple globs (absolute)
excludes = service.toExcludePaths(testDir, [join(testDir, 'node_modules', '**')]);
excludes = watcher.toExcludePaths(testDir, [join(testDir, 'node_modules', '**')]);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], join(testDir, 'node_modules'));
// Linux: more restrictive glob treatment
if (isLinux) {
excludes = service.toExcludePaths(testDir, ['**/node_modules/*/**']);
excludes = watcher.toExcludePaths(testDir, ['**/node_modules/*/**']);
assert.strictEqual(excludes?.length, 1);
assert.strictEqual(excludes[0], join(testDir, 'node_modules'));
}
@ -620,17 +620,17 @@ flakySuite('Recursive Watcher (parcel)', () => {
// unsupported globs
else {
excludes = service.toExcludePaths(testDir, ['**/node_modules/*/**']);
excludes = watcher.toExcludePaths(testDir, ['**/node_modules/*/**']);
assert.strictEqual(excludes, undefined);
}
excludes = service.toExcludePaths(testDir, ['**/*.js']);
excludes = watcher.toExcludePaths(testDir, ['**/*.js']);
assert.strictEqual(excludes, undefined);
excludes = service.toExcludePaths(testDir, ['*.js']);
excludes = watcher.toExcludePaths(testDir, ['*.js']);
assert.strictEqual(excludes, undefined);
excludes = service.toExcludePaths(testDir, ['*']);
excludes = watcher.toExcludePaths(testDir, ['*']);
assert.strictEqual(excludes, undefined);
});
});

View file

@ -304,8 +304,6 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native
colorScheme: IColorScheme;
autoDetectHighContrast?: boolean;
legacyWatcher?: string; // TODO@bpasero remove me once watcher is settled
perfMarks: PerformanceMark[];
filesToWait?: IPathsToWaitFor;

View file

@ -1293,7 +1293,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
os: { release: release(), hostname: hostname() },
zoomLevel: typeof windowConfig?.zoomLevel === 'number' ? windowConfig.zoomLevel : undefined,
legacyWatcher: this.configurationService.getValue('files.legacyWatcher'),
autoDetectHighContrast: windowConfig?.autoDetectHighContrast ?? true,
accessibilitySupport: app.accessibilitySupportEnabled,
colorScheme: {

View file

@ -87,7 +87,7 @@ class SessionFileWatcher extends Disposable implements ISessionFileWatcher {
}));
this._register(this.fileWatcher.onDidChangeFile(events => localChangeEmitter.fire(events)));
this._register(this.fileWatcher.onDidErrorOccur(error => sessionEmitter.fire(error)));
this._register(this.fileWatcher.onDidWatchError(error => sessionEmitter.fire(error)));
}
private getWatcherOptions(): IWatcherOptions | undefined {

View file

@ -12,8 +12,7 @@ exports.collectModules = function () {
createModuleDescription('vs/workbench/contrib/debug/node/telemetryApp'),
createModuleDescription('vs/platform/files/node/watcher/nsfw/watcherApp'),
createModuleDescription('vs/platform/files/node/watcher/parcel/watcherApp'),
createModuleDescription('vs/platform/files/node/watcher/parcel/parcelWatcherMain'),
createModuleDescription('vs/platform/terminal/node/ptyHostMain'),

View file

@ -257,22 +257,6 @@ configurationRegistry.registerConfiguration({
'description': nls.localize('watcherInclude', "Configure extra paths to watch for changes inside the workspace. By default, all workspace folders will be watched recursively, except for folders that are symbolic links. You can explicitly add absolute or relative paths to support watching folders that are symbolic links. Relative paths will be resolved to an absolute path using the currently opened workspace."),
'scope': ConfigurationScope.RESOURCE
},
'files.legacyWatcher': {
'type': 'string',
'enum': [
'on',
'off',
'default',
],
'markdownEnumDescriptions': [
nls.localize('files.legacyWatcher.on', "Enable the legacy file watcher in case you see issues with the new file watcher."),
nls.localize('files.legacyWatcher.off', "Disable the legacy file watcher and enable the new file watcher to benefit from its capabilities."),
nls.localize('files.legacyWatcher.default', "The new file watcher will be enabled."),
],
'default': 'default',
'description': nls.localize('legacyWatcher', "Controls the mechanism used for file watching. Only change this when you see issues related to file watching."),
'scope': ConfigurationScope.APPLICATION
},
'files.hotExit': hotExitConfiguration,
'files.defaultLanguage': {
'type': 'string',

View file

@ -41,7 +41,7 @@ export class WorkspaceWatcher extends Disposable {
this._register(this.contextService.onDidChangeWorkspaceFolders(e => this.onDidChangeWorkspaceFolders(e)));
this._register(this.contextService.onDidChangeWorkbenchState(() => this.onDidChangeWorkbenchState()));
this._register(this.configurationService.onDidChangeConfiguration(e => this.onDidChangeConfiguration(e)));
this._register(this.fileService.onError(error => this.onError(error)));
this._register(this.fileService.onDidWatchError(error => this.onDidWatchError(error)));
}
private onDidChangeWorkspaceFolders(e: IWorkspaceFoldersChangeEvent): void {
@ -67,7 +67,7 @@ export class WorkspaceWatcher extends Disposable {
}
}
private onError(error: Error): void {
private onDidWatchError(error: Error): void {
const msg = error.toString();
// Detect if we run into ENOSPC issues

View file

@ -26,7 +26,6 @@ interface IConfiguration extends IWindowsConfiguration {
debug?: { console?: { wordWrap?: boolean } };
editor?: { accessibilitySupport?: 'on' | 'off' | 'auto' };
security?: { workspace?: { trust?: { enabled?: boolean } } };
files?: { legacyWatcher?: string };
}
export class SettingsChangeRelauncher extends Disposable implements IWorkbenchContribution {
@ -38,7 +37,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
private updateMode: string | undefined;
private accessibilitySupport: 'on' | 'off' | 'auto' | undefined;
private workspaceTrustEnabled: boolean | undefined;
private legacyFileWatcher: string | undefined = undefined;
constructor(
@IHostService private readonly hostService: IHostService,
@ -100,12 +98,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
this.workspaceTrustEnabled = config.security.workspace.trust.enabled;
changed = true;
}
// Legacy File Watcher
if (typeof config.files?.legacyWatcher === 'string' && config.files.legacyWatcher !== this.legacyFileWatcher) {
this.legacyFileWatcher = config.files.legacyWatcher;
changed = true;
}
}
// Notify only when changed and we are the focused window (avoids notification spam across windows)

View file

@ -13,8 +13,8 @@ import { ReadableStreamEvents } from 'vs/base/common/stream';
import { URI } from 'vs/base/common/uri';
import { IPCFileSystemProvider } from 'vs/platform/files/common/ipcFileSystemProvider';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDiskFileChange, ILogMessage, WatcherService } from 'vs/platform/files/common/watcher';
import { ParcelFileWatcher } from 'vs/workbench/services/files/electron-sandbox/parcelWatcherService';
import { IDiskFileChange, ILogMessage, AbstractRecursiveWatcherClient } from 'vs/platform/files/common/watcher';
import { ParcelWatcherClient } from 'vs/workbench/services/files/electron-sandbox/parcelWatcherClient';
import { ILogService } from 'vs/platform/log/common/log';
import { ISharedProcessWorkerWorkbenchService } from 'vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessWorkerWorkbenchService';
@ -45,7 +45,7 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple
// Forward events from the embedded provider
this.provider.onDidChangeFile(e => this._onDidChangeFile.fire(e));
this.provider.onDidErrorOccur(e => this._onDidErrorOccur.fire(e));
this.provider.onDidWatchError(e => this._onDidWatchError.fire(e));
}
//#region File Capabilities
@ -137,8 +137,8 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple
onChange: (changes: IDiskFileChange[]) => void,
onLogMessage: (msg: ILogMessage) => void,
verboseLogging: boolean
): WatcherService {
return new ParcelFileWatcher(
): AbstractRecursiveWatcherClient {
return new ParcelWatcherClient(
changes => onChange(changes),
msg => onLogMessage(msg),
verboseLogging,

View file

@ -5,10 +5,10 @@
import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
import { getDelayedChannel, ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { AbstractWatcherService, IDiskFileChange, ILogMessage, IWatcherService } from 'vs/platform/files/common/watcher';
import { AbstractRecursiveWatcherClient, IDiskFileChange, ILogMessage, IRecursiveWatcher } from 'vs/platform/files/common/watcher';
import { ISharedProcessWorkerWorkbenchService } from 'vs/workbench/services/sharedProcess/electron-sandbox/sharedProcessWorkerWorkbenchService';
export class ParcelFileWatcher extends AbstractWatcherService {
export class ParcelWatcherClient extends AbstractRecursiveWatcherClient {
constructor(
onFileChanges: (changes: IDiskFileChange[]) => void,
@ -21,8 +21,8 @@ export class ParcelFileWatcher extends AbstractWatcherService {
this.init();
}
protected override createService(disposables: DisposableStore): IWatcherService {
const service = ProxyChannel.toService<IWatcherService>(getDelayedChannel((async () => {
protected override createWatcher(disposables: DisposableStore): IRecursiveWatcher {
const watcher = ProxyChannel.toService<IRecursiveWatcher>(getDelayedChannel((async () => {
// Acquire parcel watcher via shared process worker
//
@ -33,8 +33,8 @@ export class ParcelFileWatcher extends AbstractWatcherService {
// The shared process worker services ensures to terminate
// the process automatically when the window closes or reloads.
const { client, onDidTerminate } = await this.sharedProcessWorkerWorkbenchService.createWorker({
moduleId: 'vs/platform/files/node/watcher/parcel/watcherApp',
type: 'watcherServiceParcelSharedProcess'
moduleId: 'vs/platform/files/node/watcher/parcel/parcelWatcherMain',
type: 'parcelWatcher'
});
// React on unexpected termination of the watcher process
@ -54,8 +54,8 @@ export class ParcelFileWatcher extends AbstractWatcherService {
// only seem to be happening when used from Electron,
// not pure node.js.
// https://github.com/microsoft/vscode/issues/136264
disposables.add(toDisposable(() => service.stop()));
disposables.add(toDisposable(() => watcher.stop()));
return service;
return watcher;
}
}

View file

@ -6966,11 +6966,6 @@ node-addon-api@^3.2.1:
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161"
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==
node-addon-api@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87"
integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q==
node-fetch@^2.6.0:
version "2.6.6"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.6.tgz#1751a7c01834e8e1697758732e9efb6eeadfaf89"
@ -10410,13 +10405,6 @@ vscode-nls-dev@^3.3.1:
xml2js "^0.4.19"
yargs "^13.2.4"
vscode-nsfw@2.1.8:
version "2.1.8"
resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-2.1.8.tgz#88f5e56b22b2fd0be582e73eb1158ea8257f6c6c"
integrity sha512-tFnxPIuM65czw/Kjz8KXD88fIJtnCjzQ0ighS0a1yasVv6jKkANAlGffiOitTLMkDjvFCY8OyP6xjarTkpu/VQ==
dependencies:
node-addon-api "^4.2.0"
vscode-oniguruma@1.6.1:
version "1.6.1"
resolved "https://registry.yarnpkg.com/vscode-oniguruma/-/vscode-oniguruma-1.6.1.tgz#2bf4dfcfe3dd2e56eb549a3068c8ee39e6c30ce5"