mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 04:49:35 +00:00
✨ watcher - remove vscode-nsfw
legacy watcher
This commit is contained in:
parent
8d61a33e38
commit
16d0a319b2
|
@ -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/**
|
||||
|
|
|
@ -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']
|
||||
},
|
||||
{
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -22,7 +22,6 @@ export class HTMLFileSystemProvider implements IFileSystemProviderWithFileReadWr
|
|||
|
||||
readonly onDidChangeCapabilities = Event.None;
|
||||
readonly onDidChangeFile = Event.None;
|
||||
readonly onDidErrorOccur = Event.None;
|
||||
|
||||
//#endregion
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
||||
/**
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
|
@ -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}`;
|
||||
}
|
||||
}
|
|
@ -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>;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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}`;
|
||||
}
|
||||
}
|
|
@ -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')));
|
||||
}
|
||||
}
|
|
@ -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));
|
|
@ -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));
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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: {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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'),
|
||||
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
12
yarn.lock
12
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in a new issue