mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 21:09:43 +00:00
Adopt @parcel/watcher@2.0.7
(#169553)
* watcher - add debounce logic to parcel too * 🆙 `@parcel/watcher: 2.0.7` * bring back dispose * 💄 * more cleanup * tests * 💄 * same debounces
This commit is contained in:
parent
fef6e86202
commit
430e35b22c
|
@ -62,7 +62,7 @@
|
|||
"dependencies": {
|
||||
"@microsoft/1ds-core-js": "^3.2.2",
|
||||
"@microsoft/1ds-post-js": "^3.2.2",
|
||||
"@parcel/watcher": "2.0.5",
|
||||
"@parcel/watcher": "2.0.7",
|
||||
"@vscode/iconv-lite-umd": "0.7.0",
|
||||
"@vscode/ripgrep": "^1.14.2",
|
||||
"@vscode/sqlite3": "5.1.2-vscode",
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
"dependencies": {
|
||||
"@microsoft/1ds-core-js": "^3.2.2",
|
||||
"@microsoft/1ds-post-js": "^3.2.2",
|
||||
"@parcel/watcher": "2.0.5",
|
||||
"@parcel/watcher": "2.0.7",
|
||||
"@vscode/iconv-lite-umd": "0.7.0",
|
||||
"@vscode/ripgrep": "^1.14.2",
|
||||
"@vscode/vscode-languagedetection": "1.0.21",
|
||||
|
|
|
@ -38,10 +38,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.6.tgz#6fe03468862861f5f88ac4c3959a652b3797f1bc"
|
||||
integrity sha512-D1Oivw1A4bIXhzBIy3/BBPn3p2On+kpO2NiYt9shICDK7L/w+cR6FFBUsBZ05l6iqzTeL+Jm8lAYn0g6G7DmDg==
|
||||
|
||||
"@parcel/watcher@2.0.5":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.5.tgz#f913a54e1601b0aac972803829b0eece48de215b"
|
||||
integrity sha512-x0hUbjv891omnkcHD7ZOhiyyUqUUR6MNjq89JhEI3BxppeKWAm6NPQsqqRrAkCJBogdT/o/My21sXtTI9rJIsw==
|
||||
"@parcel/watcher@2.0.7":
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.7.tgz#c95fe1370e8c6237cb9729c9c075264acc7e21a5"
|
||||
integrity sha512-gc3hoS6e+2XdIQ4HHljDB1l0Yx2EWh/sBBtCEFNKGSMlwASWeAQsOY/fPbxOBcZ/pg0jBh4Ga+4xHlZc4faAEQ==
|
||||
dependencies:
|
||||
node-addon-api "^3.2.1"
|
||||
node-gyp-build "^4.3.0"
|
||||
|
|
|
@ -875,6 +875,13 @@ export class RunOnceScheduler implements IDisposable {
|
|||
return this.timeoutToken !== -1;
|
||||
}
|
||||
|
||||
flush(): void {
|
||||
if (this.isScheduled()) {
|
||||
this.cancel();
|
||||
this.doRun();
|
||||
}
|
||||
}
|
||||
|
||||
private onTimeout() {
|
||||
this.timeoutToken = -1;
|
||||
if (this.runner) {
|
||||
|
@ -961,6 +968,7 @@ export class ProcessTimeRunOnceScheduler {
|
|||
}
|
||||
|
||||
export class RunOnceWorker<T> extends RunOnceScheduler {
|
||||
|
||||
private units: T[] = [];
|
||||
|
||||
constructor(runner: (units: T[]) => void, timeout: number) {
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { watch } from 'fs';
|
||||
import { ThrottledDelayer, ThrottledWorker } from 'vs/base/common/async';
|
||||
import { RunOnceWorker, ThrottledWorker } from 'vs/base/common/async';
|
||||
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
|
||||
import { isEqualOrParent } from 'vs/base/common/extpath';
|
||||
import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
|
@ -26,25 +26,26 @@ export class NodeJSFileWatcherLibrary extends Disposable {
|
|||
|
||||
// A delay for collecting file changes from node.js
|
||||
// before collecting them for coalescing and emitting
|
||||
// (same delay as Parcel is using)
|
||||
private static readonly FILE_CHANGES_HANDLER_DELAY = 50;
|
||||
// Same delay as used for the recursive watcher.
|
||||
private static readonly FILE_CHANGES_HANDLER_DELAY = 75;
|
||||
|
||||
// Reduce likelyhood of spam from file events via throttling.
|
||||
// These numbers are a bit more aggressive compared to the
|
||||
// recursive watcher because we can have many individual
|
||||
// node.js watchers per request.
|
||||
// (https://github.com/microsoft/vscode/issues/124723)
|
||||
private readonly throttledFileChangesWorker = new ThrottledWorker<IDiskFileChange>(
|
||||
private readonly throttledFileChangesEmitter = this._register(new ThrottledWorker<IDiskFileChange>(
|
||||
{
|
||||
maxWorkChunkSize: 100, // only process up to 100 changes at once before...
|
||||
throttleDelay: 200, // ...resting for 200ms until we process events again...
|
||||
maxBufferedWork: 10000 // ...but never buffering more than 10000 events in memory
|
||||
},
|
||||
events => this.onDidFilesChange(events)
|
||||
);
|
||||
));
|
||||
|
||||
private readonly fileChangesDelayer = this._register(new ThrottledDelayer<void>(NodeJSFileWatcherLibrary.FILE_CHANGES_HANDLER_DELAY));
|
||||
private fileChangesBuffer: IDiskFileChange[] = [];
|
||||
// Aggregate file changes over FILE_CHANGES_HANDLER_DELAY
|
||||
// to coalesce events and reduce spam.
|
||||
private readonly fileChangesAggregator = this._register(new RunOnceWorker<IDiskFileChange>(events => this.handleFileChanges(events), NodeJSFileWatcherLibrary.FILE_CHANGES_HANDLER_DELAY));
|
||||
|
||||
private readonly excludes = parseWatcherPatterns(this.request.path, this.request.excludes);
|
||||
private readonly includes = this.request.includes ? parseWatcherPatterns(this.request.path, this.request.includes) : undefined;
|
||||
|
@ -324,12 +325,13 @@ export class NodeJSFileWatcherLibrary extends Disposable {
|
|||
|
||||
// File seems to be really gone, so emit a deleted event and dispose
|
||||
else {
|
||||
const eventPromise = this.onFileChange({ path: this.request.path, type: FileChangeType.DELETED }, true /* skip excludes/includes (file is explicitly watched) */);
|
||||
this.onFileChange({ path: this.request.path, type: FileChangeType.DELETED }, true /* skip excludes/includes (file is explicitly watched) */);
|
||||
|
||||
// Important to await the event delivery
|
||||
// Important to flush the event delivery
|
||||
// before disposing the watcher, otherwise
|
||||
// we will loose this event.
|
||||
await eventPromise;
|
||||
this.fileChangesAggregator.flush();
|
||||
|
||||
this.dispose();
|
||||
}
|
||||
}, NodeJSFileWatcherLibrary.FILE_DELETE_HANDLER_DELAY);
|
||||
|
@ -358,7 +360,7 @@ export class NodeJSFileWatcherLibrary extends Disposable {
|
|||
});
|
||||
}
|
||||
|
||||
private async onFileChange(event: IDiskFileChange, skipIncludeExcludeChecks = false): Promise<void> {
|
||||
private onFileChange(event: IDiskFileChange, skipIncludeExcludeChecks = false): void {
|
||||
if (this.cts.token.isCancellationRequested) {
|
||||
return;
|
||||
}
|
||||
|
@ -368,7 +370,7 @@ export class NodeJSFileWatcherLibrary extends Disposable {
|
|||
this.trace(`${event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${event.path}`);
|
||||
}
|
||||
|
||||
// Add to buffer unless excluded or not included (not if explicitly disabled)
|
||||
// Add to aggregator unless excluded or not included (not if explicitly disabled)
|
||||
if (!skipIncludeExcludeChecks && this.excludes.some(exclude => exclude(event.path))) {
|
||||
if (this.verboseLogging) {
|
||||
this.trace(` >> ignored (excluded) ${event.path}`);
|
||||
|
@ -378,42 +380,34 @@ export class NodeJSFileWatcherLibrary extends Disposable {
|
|||
this.trace(` >> ignored (not included) ${event.path}`);
|
||||
}
|
||||
} else {
|
||||
this.fileChangesBuffer.push(event);
|
||||
this.fileChangesAggregator.work(event);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle emit through delayer to accommodate for bulk changes and thus reduce spam
|
||||
try {
|
||||
await this.fileChangesDelayer.trigger(async () => {
|
||||
const fileChanges = this.fileChangesBuffer;
|
||||
this.fileChangesBuffer = [];
|
||||
private handleFileChanges(fileChanges: IDiskFileChange[]): void {
|
||||
|
||||
// Coalesce events: merge events of same kind
|
||||
const coalescedFileChanges = coalesceEvents(fileChanges);
|
||||
// Coalesce events: merge events of same kind
|
||||
const coalescedFileChanges = coalesceEvents(fileChanges);
|
||||
if (coalescedFileChanges.length > 0) {
|
||||
|
||||
if (coalescedFileChanges.length > 0) {
|
||||
|
||||
// Logging
|
||||
if (this.verboseLogging) {
|
||||
for (const event of coalescedFileChanges) {
|
||||
this.trace(`>> normalized ${event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${event.path}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast to clients via throttler
|
||||
const worked = this.throttledFileChangesWorker.work(coalescedFileChanges);
|
||||
|
||||
// Logging
|
||||
if (!worked) {
|
||||
this.warn(`started ignoring events due to too many file change events at once (incoming: ${coalescedFileChanges.length}, most recent change: ${coalescedFileChanges[0].path}). Use 'files.watcherExclude' setting to exclude folders with lots of changing files (e.g. compilation output).`);
|
||||
} else {
|
||||
if (this.throttledFileChangesWorker.pending > 0) {
|
||||
this.trace(`started throttling events due to large amount of file change events at once (pending: ${this.throttledFileChangesWorker.pending}, most recent change: ${coalescedFileChanges[0].path}). Use 'files.watcherExclude' setting to exclude folders with lots of changing files (e.g. compilation output).`);
|
||||
}
|
||||
}
|
||||
// Logging
|
||||
if (this.verboseLogging) {
|
||||
for (const event of coalescedFileChanges) {
|
||||
this.trace(`>> normalized ${event.type === FileChangeType.ADDED ? '[ADDED]' : event.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${event.path}`);
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// ignore (we are likely disposed and cancelled)
|
||||
}
|
||||
|
||||
// Broadcast to clients via throttled emitter
|
||||
const worked = this.throttledFileChangesEmitter.work(coalescedFileChanges);
|
||||
|
||||
// Logging
|
||||
if (!worked) {
|
||||
this.warn(`started ignoring events due to too many file change events at once (incoming: ${coalescedFileChanges.length}, most recent change: ${coalescedFileChanges[0].path}). Use 'files.watcherExclude' setting to exclude folders with lots of changing files (e.g. compilation output).`);
|
||||
} else {
|
||||
if (this.throttledFileChangesEmitter.pending > 0) {
|
||||
this.trace(`started throttling events due to large amount of file change events at once (pending: ${this.throttledFileChangesEmitter.pending}, most recent change: ${coalescedFileChanges[0].path}). Use 'files.watcherExclude' setting to exclude folders with lots of changing files (e.g. compilation output).`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import * as parcelWatcher from '@parcel/watcher';
|
||||
import { existsSync, statSync, unlinkSync } from 'fs';
|
||||
import { tmpdir } from 'os';
|
||||
import { DeferredPromise, RunOnceScheduler, ThrottledWorker } from 'vs/base/common/async';
|
||||
import { DeferredPromise, RunOnceScheduler, RunOnceWorker, ThrottledWorker } 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';
|
||||
|
@ -88,16 +88,32 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher {
|
|||
|
||||
protected readonly watchers = new Map<string, IParcelWatcherInstance>();
|
||||
|
||||
// A delay for collecting file changes from Parcel
|
||||
// before collecting them for coalescing and emitting.
|
||||
// Parcel internally uses 50ms as delay, so we use 75ms,
|
||||
// to schedule sufficiently after Parcel.
|
||||
//
|
||||
// Note: since Parcel 2.0.7, the very first event is
|
||||
// emitted without delay if no events occured over a
|
||||
// duration of 500ms. But we always want to aggregate
|
||||
// events to apply our coleasing logic.
|
||||
//
|
||||
private static readonly FILE_CHANGES_HANDLER_DELAY = 75;
|
||||
|
||||
// Reduce likelyhood of spam from file events via throttling.
|
||||
// (https://github.com/microsoft/vscode/issues/124723)
|
||||
private readonly throttledFileChangesWorker = new ThrottledWorker<IDiskFileChange>(
|
||||
private readonly throttledFileChangesEmitter = this._register(new ThrottledWorker<IDiskFileChange>(
|
||||
{
|
||||
maxWorkChunkSize: 500, // only process up to 500 changes at once before...
|
||||
throttleDelay: 200, // ...resting for 200ms until we process events again...
|
||||
maxBufferedWork: 30000 // ...but never buffering more than 30000 events in memory
|
||||
},
|
||||
events => this._onDidChangeFile.fire(events)
|
||||
);
|
||||
));
|
||||
|
||||
// Aggregate file changes over FILE_CHANGES_HANDLER_DELAY
|
||||
// to coalesce events and reduce duplicate events.
|
||||
private readonly fileChangesAggregator = this._register(new RunOnceWorker<IDiskFileChange>(events => this.handleParcelEvents(events), ParcelWatcher.FILE_CHANGES_HANDLER_DELAY));
|
||||
|
||||
private verboseLogging = false;
|
||||
private enospcErrorLogged = false;
|
||||
|
@ -395,23 +411,14 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher {
|
|||
// Normalize events: handle NFC normalization and symlinks
|
||||
// It is important to do this before checking for includes
|
||||
// and excludes to check on the original path.
|
||||
const { events: normalizedEvents, rootDeleted } = this.normalizeEvents(parcelEvents, watcher.request, realPathDiffers, realPathLength);
|
||||
this.normalizeEvents(parcelEvents, watcher.request, realPathDiffers, realPathLength);
|
||||
|
||||
// Check for excludes
|
||||
const includedEvents = this.handleExcludeIncludes(normalizedEvents, excludes, includes);
|
||||
const includedEvents = this.handleExcludeIncludes(parcelEvents, excludes, includes);
|
||||
|
||||
// Coalesce events: merge events of same kind
|
||||
const coalescedEvents = coalesceEvents(includedEvents);
|
||||
|
||||
// Filter events: check for specific events we want to exclude
|
||||
const filteredEvents = this.filterEvents(coalescedEvents, watcher.request, rootDeleted);
|
||||
|
||||
// Broadcast to clients
|
||||
this.emitEvents(filteredEvents);
|
||||
|
||||
// Handle root path delete if confirmed from coalesced events
|
||||
if (rootDeleted && coalescedEvents.some(event => event.path === watcher.request.path && event.type === FileChangeType.DELETED)) {
|
||||
this.onWatchedPathDeleted(watcher);
|
||||
// Add to aggregator for later processing
|
||||
for (const includedEvent of includedEvents) {
|
||||
this.fileChangesAggregator.work(includedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -441,6 +448,25 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher {
|
|||
return events;
|
||||
}
|
||||
|
||||
private handleParcelEvents(parcelEvents: IDiskFileChange[]): void {
|
||||
|
||||
// Coalesce events: merge events of same kind
|
||||
const coalescedEvents = coalesceEvents(parcelEvents);
|
||||
|
||||
// Filter events: check for specific events we want to exclude
|
||||
const { events: filteredEvents, deletedRoots } = this.filterEvents(coalescedEvents);
|
||||
|
||||
// Broadcast to clients
|
||||
this.emitEvents(filteredEvents);
|
||||
|
||||
// Handle root path deletes
|
||||
if (deletedRoots) {
|
||||
for (const deletedRoot of deletedRoots) {
|
||||
this.onWatchedPathDeleted(deletedRoot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private emitEvents(events: IDiskFileChange[]): void {
|
||||
if (events.length === 0) {
|
||||
return;
|
||||
|
@ -454,14 +480,14 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher {
|
|||
}
|
||||
|
||||
// Broadcast to clients via throttler
|
||||
const worked = this.throttledFileChangesWorker.work(events);
|
||||
const worked = this.throttledFileChangesEmitter.work(events);
|
||||
|
||||
// Logging
|
||||
if (!worked) {
|
||||
this.warn(`started ignoring events due to too many file change events at once (incoming: ${events.length}, most recent change: ${events[0].path}). Use 'files.watcherExclude' setting to exclude folders with lots of changing files (e.g. compilation output).`);
|
||||
} else {
|
||||
if (this.throttledFileChangesWorker.pending > 0) {
|
||||
this.trace(`started throttling events due to large amount of file change events at once (pending: ${this.throttledFileChangesWorker.pending}, most recent change: ${events[0].path}). Use 'files.watcherExclude' setting to exclude folders with lots of changing files (e.g. compilation output).`);
|
||||
if (this.throttledFileChangesEmitter.pending > 0) {
|
||||
this.trace(`started throttling events due to large amount of file change events at once (pending: ${this.throttledFileChangesEmitter.pending}, most recent change: ${events[0].path}). Use 'files.watcherExclude' setting to exclude folders with lots of changing files (e.g. compilation output).`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -496,9 +522,7 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher {
|
|||
return { realPath, realPathDiffers, realPathLength };
|
||||
}
|
||||
|
||||
private normalizeEvents(events: parcelWatcher.Event[], request: IRecursiveWatchRequest, realPathDiffers: boolean, realPathLength: number): { events: parcelWatcher.Event[]; rootDeleted: boolean } {
|
||||
let rootDeleted = false;
|
||||
|
||||
private normalizeEvents(events: parcelWatcher.Event[], request: IRecursiveWatchRequest, realPathDiffers: boolean, realPathLength: number): void {
|
||||
for (const event of events) {
|
||||
|
||||
// Mac uses NFD unicode form on disk, but we want NFC
|
||||
|
@ -518,33 +542,33 @@ export class ParcelWatcher extends Disposable implements IRecursiveWatcher {
|
|||
if (realPathDiffers) {
|
||||
event.path = request.path + event.path.substr(realPathLength);
|
||||
}
|
||||
|
||||
// Check for root deleted
|
||||
if (event.path === request.path && event.type === 'delete') {
|
||||
rootDeleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
return { events, rootDeleted };
|
||||
}
|
||||
|
||||
private filterEvents(events: IDiskFileChange[], request: IRecursiveWatchRequest, rootDeleted: boolean): IDiskFileChange[] {
|
||||
if (!rootDeleted) {
|
||||
return events;
|
||||
}
|
||||
private filterEvents(events: IDiskFileChange[]): { events: IDiskFileChange[]; deletedRoots?: Set<IParcelWatcherInstance> } {
|
||||
const filteredEvents: IDiskFileChange[] = [];
|
||||
let deletedRoots: Set<IParcelWatcherInstance> | undefined = undefined;
|
||||
|
||||
for (const event of events) {
|
||||
if (event.type === FileChangeType.DELETED && this.watchers.has(event.path)) {
|
||||
|
||||
return events.filter(event => {
|
||||
if (event.path === request.path && event.type === FileChangeType.DELETED) {
|
||||
// Explicitly exclude changes to root if we have any
|
||||
// to avoid VS Code closing all opened editors which
|
||||
// can happen e.g. in case of network connectivity
|
||||
// issues
|
||||
// (https://github.com/microsoft/vscode/issues/136673)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
if (!deletedRoots) {
|
||||
deletedRoots = new Set();
|
||||
}
|
||||
|
||||
deletedRoots.add(this.watchers.get(event.path)!);
|
||||
} else {
|
||||
filteredEvents.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
return { events: filteredEvents, deletedRoots };
|
||||
}
|
||||
|
||||
private onWatchedPathDeleted(watcher: IParcelWatcherInstance): void {
|
||||
|
|
|
@ -64,6 +64,7 @@ import { NodeJSWatcher } from 'vs/platform/files/node/watcher/nodejs/nodejsWatch
|
|||
|
||||
setup(async () => {
|
||||
watcher = new TestNodeJSWatcher();
|
||||
watcher?.setVerboseLogging(loggingEnabled);
|
||||
|
||||
watcher.onDidLogMessage(e => {
|
||||
if (loggingEnabled) {
|
||||
|
|
|
@ -13,7 +13,7 @@ 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 { ParcelWatcher } from 'vs/platform/files/node/watcher/parcel/parcelWatcher';
|
||||
import { IRecursiveWatchRequest } from 'vs/platform/files/common/watcher';
|
||||
import { IDiskFileChange, IRecursiveWatchRequest } from 'vs/platform/files/common/watcher';
|
||||
import { getDriveLetter } from 'vs/base/common/extpath';
|
||||
import { ltrim } from 'vs/base/common/strings';
|
||||
|
||||
|
@ -66,6 +66,7 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
|
||||
setup(async () => {
|
||||
watcher = new TestParcelWatcher();
|
||||
watcher.setVerboseLogging(loggingEnabled);
|
||||
|
||||
watcher.onDidLogMessage(e => {
|
||||
if (loggingEnabled) {
|
||||
|
@ -105,13 +106,13 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
}
|
||||
}
|
||||
|
||||
async function awaitEvent(service: TestParcelWatcher, path: string, type: FileChangeType, failOnEventReason?: string): Promise<void> {
|
||||
async function awaitEvent(service: TestParcelWatcher, path: string, type: FileChangeType, failOnEventReason?: string): Promise<IDiskFileChange[]> {
|
||||
if (loggingEnabled) {
|
||||
console.log(`Awaiting change type '${toMsg(type)}' on file '${path}'`);
|
||||
}
|
||||
|
||||
// Await the event
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
const res = await new Promise<IDiskFileChange[]>((resolve, reject) => {
|
||||
const disposable = service.onDidChangeFile(events => {
|
||||
for (const event of events) {
|
||||
if (event.path === path && event.type === type) {
|
||||
|
@ -119,7 +120,7 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
if (failOnEventReason) {
|
||||
reject(new Error(`Unexpected file event: ${failOnEventReason}`));
|
||||
} else {
|
||||
setImmediate(() => resolve()); // copied from parcel watcher tests, seems to drop unrelated events on macOS
|
||||
setImmediate(() => resolve(events)); // copied from parcel watcher tests, seems to drop unrelated events on macOS
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -132,6 +133,8 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
// change event
|
||||
// Refs: https://github.com/microsoft/vscode/issues/137430
|
||||
await timeout(1);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
function awaitMessage(service: TestParcelWatcher, type: 'trace' | 'warn' | 'error' | 'info' | 'debug'): Promise<void> {
|
||||
|
@ -244,26 +247,20 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
await Promises.writeFile(anotherNewFilePath, 'Hello Another World');
|
||||
await changeFuture;
|
||||
|
||||
// Skip following asserts on macOS where the fs-events service
|
||||
// does not really give a full guarantee about the correlation
|
||||
// of an event to a change.
|
||||
if (!isMacintosh) {
|
||||
// Read file does not emit event
|
||||
changeFuture = awaitEvent(watcher, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-read-file');
|
||||
await Promises.readFile(anotherNewFilePath);
|
||||
await Promise.race([timeout(100), changeFuture]);
|
||||
|
||||
// Read file does not emit event
|
||||
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(watcher, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
|
||||
await Promises.stat(anotherNewFilePath);
|
||||
await Promise.race([timeout(100), changeFuture]);
|
||||
|
||||
// Stat file does not emit event
|
||||
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(watcher, copiedFolderpath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
|
||||
await Promises.stat(copiedFolderpath);
|
||||
await Promise.race([timeout(100), changeFuture]);
|
||||
}
|
||||
// Stat folder does not emit event
|
||||
changeFuture = awaitEvent(watcher, copiedFolderpath, FileChangeType.UPDATED, 'unexpected-event-from-stat');
|
||||
await Promises.stat(copiedFolderpath);
|
||||
await Promise.race([timeout(100), changeFuture]);
|
||||
|
||||
// Delete file
|
||||
changeFuture = awaitEvent(watcher, copiedFilepath, FileChangeType.DELETED);
|
||||
|
@ -281,7 +278,7 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
|
||||
// Delete + Recreate file
|
||||
const newFilePath = join(testDir, 'deep', 'conway.js');
|
||||
const changeFuture: Promise<unknown> = awaitEvent(watcher, newFilePath, FileChangeType.UPDATED);
|
||||
const changeFuture = awaitEvent(watcher, newFilePath, FileChangeType.UPDATED);
|
||||
await Promises.unlink(newFilePath);
|
||||
Promises.writeFile(newFilePath, 'Hello Atomic World');
|
||||
await changeFuture;
|
||||
|
@ -296,7 +293,7 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
async function basicCrudTest(filePath: string): Promise<void> {
|
||||
|
||||
// New file
|
||||
let changeFuture: Promise<unknown> = awaitEvent(watcher, filePath, FileChangeType.ADDED);
|
||||
let changeFuture = awaitEvent(watcher, filePath, FileChangeType.ADDED);
|
||||
await Promises.writeFile(filePath, 'Hello World');
|
||||
await changeFuture;
|
||||
|
||||
|
@ -324,12 +321,12 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
const newFilePath5 = join(testDir, 'deep-multiple', 'newFile-2.txt');
|
||||
const newFilePath6 = join(testDir, 'deep-multiple', 'newFile-3.txt');
|
||||
|
||||
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);
|
||||
const addedFuture1 = awaitEvent(watcher, newFilePath1, FileChangeType.ADDED);
|
||||
const addedFuture2 = awaitEvent(watcher, newFilePath2, FileChangeType.ADDED);
|
||||
const addedFuture3 = awaitEvent(watcher, newFilePath3, FileChangeType.ADDED);
|
||||
const addedFuture4 = awaitEvent(watcher, newFilePath4, FileChangeType.ADDED);
|
||||
const addedFuture5 = awaitEvent(watcher, newFilePath5, FileChangeType.ADDED);
|
||||
const addedFuture6 = awaitEvent(watcher, newFilePath6, FileChangeType.ADDED);
|
||||
|
||||
await Promise.all([
|
||||
await Promises.writeFile(newFilePath1, 'Hello World 1'),
|
||||
|
@ -344,12 +341,12 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
|
||||
// multiple change
|
||||
|
||||
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);
|
||||
const changeFuture1 = awaitEvent(watcher, newFilePath1, FileChangeType.UPDATED);
|
||||
const changeFuture2 = awaitEvent(watcher, newFilePath2, FileChangeType.UPDATED);
|
||||
const changeFuture3 = awaitEvent(watcher, newFilePath3, FileChangeType.UPDATED);
|
||||
const changeFuture4 = awaitEvent(watcher, newFilePath4, FileChangeType.UPDATED);
|
||||
const changeFuture5 = awaitEvent(watcher, newFilePath5, FileChangeType.UPDATED);
|
||||
const changeFuture6 = awaitEvent(watcher, newFilePath6, FileChangeType.UPDATED);
|
||||
|
||||
await Promise.all([
|
||||
await Promises.writeFile(newFilePath1, 'Hello Update 1'),
|
||||
|
@ -364,10 +361,10 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
|
||||
// copy with multiple files
|
||||
|
||||
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);
|
||||
const copyFuture1 = awaitEvent(watcher, join(testDir, 'deep-multiple-copy', 'newFile-1.txt'), FileChangeType.ADDED);
|
||||
const copyFuture2 = awaitEvent(watcher, join(testDir, 'deep-multiple-copy', 'newFile-2.txt'), FileChangeType.ADDED);
|
||||
const copyFuture3 = awaitEvent(watcher, join(testDir, 'deep-multiple-copy', 'newFile-3.txt'), FileChangeType.ADDED);
|
||||
const copyFuture4 = awaitEvent(watcher, join(testDir, 'deep-multiple-copy'), FileChangeType.ADDED);
|
||||
|
||||
await Promises.copy(join(testDir, 'deep-multiple'), join(testDir, 'deep-multiple-copy'), { preserveSymlinks: false });
|
||||
|
||||
|
@ -375,12 +372,12 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
|
||||
// multiple delete (single files)
|
||||
|
||||
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);
|
||||
const deleteFuture1 = awaitEvent(watcher, newFilePath1, FileChangeType.DELETED);
|
||||
const deleteFuture2 = awaitEvent(watcher, newFilePath2, FileChangeType.DELETED);
|
||||
const deleteFuture3 = awaitEvent(watcher, newFilePath3, FileChangeType.DELETED);
|
||||
const deleteFuture4 = awaitEvent(watcher, newFilePath4, FileChangeType.DELETED);
|
||||
const deleteFuture5 = awaitEvent(watcher, newFilePath5, FileChangeType.DELETED);
|
||||
const deleteFuture6 = awaitEvent(watcher, newFilePath6, FileChangeType.DELETED);
|
||||
|
||||
await Promise.all([
|
||||
await Promises.unlink(newFilePath1),
|
||||
|
@ -395,8 +392,8 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
|
||||
// multiple delete (folder)
|
||||
|
||||
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);
|
||||
const deleteFolderFuture1 = awaitEvent(watcher, join(testDir, 'deep-multiple'), FileChangeType.DELETED);
|
||||
const deleteFolderFuture2 = 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)]);
|
||||
|
||||
|
@ -408,7 +405,7 @@ import { ltrim } from 'vs/base/common/strings';
|
|||
|
||||
// New file (*.txt)
|
||||
let newTextFilePath = join(testDir, 'deep', 'newFile.txt');
|
||||
let changeFuture: Promise<unknown> = awaitEvent(watcher, newTextFilePath, FileChangeType.ADDED);
|
||||
let changeFuture = awaitEvent(watcher, newTextFilePath, FileChangeType.ADDED);
|
||||
await Promises.writeFile(newTextFilePath, 'Hello World');
|
||||
await changeFuture;
|
||||
|
||||
|
|
|
@ -628,10 +628,10 @@
|
|||
resolved "https://registry.yarnpkg.com/@opentelemetry/api/-/api-1.0.3.tgz#13a12ae9e05c2a782f7b5e84c3cbfda4225eaf80"
|
||||
integrity sha512-puWxACExDe9nxbBB3lOymQFrLYml2dVOrd7USiVRnSbgXE+KwBu+HxFvxrzfqsiSda9IWsXJG1ef7C1O2/GmKQ==
|
||||
|
||||
"@parcel/watcher@2.0.5":
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.5.tgz#f913a54e1601b0aac972803829b0eece48de215b"
|
||||
integrity sha512-x0hUbjv891omnkcHD7ZOhiyyUqUUR6MNjq89JhEI3BxppeKWAm6NPQsqqRrAkCJBogdT/o/My21sXtTI9rJIsw==
|
||||
"@parcel/watcher@2.0.7":
|
||||
version "2.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.0.7.tgz#c95fe1370e8c6237cb9729c9c075264acc7e21a5"
|
||||
integrity sha512-gc3hoS6e+2XdIQ4HHljDB1l0Yx2EWh/sBBtCEFNKGSMlwASWeAQsOY/fPbxOBcZ/pg0jBh4Ga+4xHlZc4faAEQ==
|
||||
dependencies:
|
||||
node-addon-api "^3.2.1"
|
||||
node-gyp-build "^4.3.0"
|
||||
|
|
Loading…
Reference in a new issue