mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 21:39:43 +00:00
Update nsfw
and watcher service (#133826)
* 🆙 `vscode-nsfw@2.1.5` and tweak watcher service
* update again
This commit is contained in:
parent
8c6e828a8e
commit
5991e3c4b9
|
@ -78,7 +78,7 @@
|
|||
"sudo-prompt": "9.2.1",
|
||||
"tas-client-umd": "0.1.4",
|
||||
"v8-inspect-profiler": "^0.0.22",
|
||||
"vscode-nsfw": "2.1.4",
|
||||
"vscode-nsfw": "2.1.6",
|
||||
"vscode-oniguruma": "1.5.1",
|
||||
"vscode-proxy-agent": "^0.11.0",
|
||||
"vscode-regexpp": "^3.1.0",
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
"node-pty": "0.11.0-beta7",
|
||||
"spdlog": "^0.13.0",
|
||||
"tas-client-umd": "0.1.4",
|
||||
"vscode-nsfw": "2.1.4",
|
||||
"vscode-nsfw": "2.1.6",
|
||||
"vscode-oniguruma": "1.5.1",
|
||||
"vscode-proxy-agent": "^0.11.0",
|
||||
"vscode-regexpp": "^3.1.0",
|
||||
|
|
|
@ -556,10 +556,10 @@ 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.4:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-2.1.4.tgz#8965413e41a7099e36813921d3b468c12d1b6e59"
|
||||
integrity sha512-EsYWpACwojUjLgJFPhtnk/omId+oyuHpctWQiCLblf53eOxzYC7HT30NljEigl/sFmuTZM0IEMrsonj3GKDYPw==
|
||||
vscode-nsfw@2.1.6:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-2.1.6.tgz#51148fad0e7ff3b72fe0b3500903ab24af00f308"
|
||||
integrity sha512-3RCPQ1/yBvwJuaUG6bJMOcmlRKuDHFn2xx04u8hWYOkNQODK7LncP5qXwU/NNN32/Vgp/cDy4P1o5HoGAHFOfg==
|
||||
dependencies:
|
||||
node-addon-api "*"
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as nsfw from 'vscode-nsfw';
|
||||
import { existsSync } from 'fs';
|
||||
import { RunOnceScheduler, ThrottledDelayer } from 'vs/base/common/async';
|
||||
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';
|
||||
|
@ -52,8 +52,6 @@ interface IWatcher extends IDisposable {
|
|||
|
||||
export class NsfwWatcherService extends Disposable implements IWatcherService {
|
||||
|
||||
private static readonly FS_EVENT_DELAY = 50; // aggregate and only emit events when changes have stopped for this duration (in ms)
|
||||
|
||||
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>(
|
||||
|
@ -134,9 +132,6 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
private startWatching(request: IWatchRequest, restarts = 0): void {
|
||||
const cts = new CancellationTokenSource();
|
||||
|
||||
let undeliveredFileEvents: IDiskFileChange[] = [];
|
||||
const fileEventDelayer = new ThrottledDelayer<void>(NsfwWatcherService.FS_EVENT_DELAY);
|
||||
|
||||
let nsfwPromiseResolve: (watcher: nsfw.NSFW) => void;
|
||||
const instance = new Promise<nsfw.NSFW>(resolve => nsfwPromiseResolve = resolve);
|
||||
|
||||
|
@ -149,7 +144,6 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
token: cts.token,
|
||||
dispose: () => {
|
||||
cts.dispose(true);
|
||||
fileEventDelayer.dispose();
|
||||
instance.then(instance => instance.stop());
|
||||
}
|
||||
};
|
||||
|
@ -158,7 +152,9 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
// Path checks for symbolic links / wrong casing
|
||||
const { realBasePathDiffers, realBasePathLength } = this.checkRequest(request);
|
||||
|
||||
const onFileEvent = (path: string, type: FileChangeType) => {
|
||||
let undeliveredFileEvents: IDiskFileChange[] = [];
|
||||
|
||||
const onRawFileEvent = (path: string, type: FileChangeType) => {
|
||||
if (!this.isPathIgnored(path, watcher.ignored)) {
|
||||
undeliveredFileEvents.push({ type, path });
|
||||
} else if (this.verboseLogging) {
|
||||
|
@ -167,12 +163,13 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
};
|
||||
|
||||
nsfw(request.path, events => {
|
||||
for (const event of events) {
|
||||
if (watcher.token.isCancellationRequested) {
|
||||
break; // return early when disposed
|
||||
}
|
||||
if (watcher.token.isCancellationRequested) {
|
||||
return; // return early when disposed
|
||||
}
|
||||
|
||||
// Logging
|
||||
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 ? '[CREATED]' : event.action === nsfw.actions.DELETED ? '[DELETED]' : event.action === nsfw.actions.MODIFIED ? '[CHANGED]' : '[RENAMED]'} ${logPath}`);
|
||||
|
@ -180,30 +177,23 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
|
||||
// Rename: convert into DELETE & ADD
|
||||
if (event.action === nsfw.actions.RENAMED) {
|
||||
onFileEvent(join(event.directory, event.oldFile || ''), FileChangeType.DELETED); // Rename fires when a file's name changes within a single directory
|
||||
onFileEvent(join(event.newDirectory || event.directory, event.newFile || ''), FileChangeType.ADDED);
|
||||
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: taks as is
|
||||
// Created, modified, deleted: take as is
|
||||
else {
|
||||
onFileEvent(join(event.directory, event.file || ''), NsfwWatcherService.MAP_NSFW_ACTION_TO_FILE_CHANGE.get(event.action)!);
|
||||
onRawFileEvent(join(event.directory, event.file || ''), NsfwWatcherService.MAP_NSFW_ACTION_TO_FILE_CHANGE.get(event.action)!);
|
||||
}
|
||||
}
|
||||
|
||||
// Send events delayed and normalized
|
||||
fileEventDelayer.trigger(async () => {
|
||||
if (watcher.token.isCancellationRequested) {
|
||||
return; // return early when disposed
|
||||
}
|
||||
// Reset undelivered events array
|
||||
const undeliveredFileEventsToEmit = undeliveredFileEvents;
|
||||
undeliveredFileEvents = [];
|
||||
|
||||
// Remember as delivered
|
||||
const events = undeliveredFileEvents;
|
||||
undeliveredFileEvents = [];
|
||||
|
||||
// Broadcast to clients normalized
|
||||
const normalizedEvents = normalizeFileChanges(this.normalizeEvents(events, request, realBasePathDiffers, realBasePathLength));
|
||||
this.emitEvents(normalizedEvents);
|
||||
});
|
||||
// Broadcast to clients normalized
|
||||
const normalizedEvents = normalizeFileChanges(this.normalizeEvents(undeliveredFileEventsToEmit, request, realBasePathDiffers, realBasePathLength));
|
||||
this.emitEvents(normalizedEvents);
|
||||
}, this.getOptions(watcher)).then(async nsfwWatcher => {
|
||||
|
||||
// Begin watching unless disposed already
|
||||
|
@ -230,9 +220,9 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
}
|
||||
},
|
||||
|
||||
// The default delay of NSFW is 500 but we already do some
|
||||
// debouncing in our code so we reduce the delay slightly
|
||||
debounceMS: 100
|
||||
// The default delay of NSFW is 500 but we want to
|
||||
// react a bit faster than that.
|
||||
debounceMS: 250
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -309,36 +299,33 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
// See https://github.com/microsoft/vscode/issues/7950
|
||||
if (msg.indexOf('Inotify limit reached') !== -1) {
|
||||
if (!this.enospcErrorLogged) {
|
||||
this.enospcErrorLogged = true; // only log this error once to protect against log spam
|
||||
this.error('Inotify limit reached (ENOSPC)', watcher);
|
||||
|
||||
this.enospcErrorLogged = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Specially handle this error that indicates the watcher
|
||||
// has stopped and we need to restart it.
|
||||
else if (msg.indexOf('Service shutdown unexpectedly') !== -1) {
|
||||
const handled = this.onUnexpectedShutdown(watcher);
|
||||
if (!handled) {
|
||||
this.error('Watcher service shutdown unexpectedly (ESHUTDOWN)', watcher);
|
||||
}
|
||||
}
|
||||
|
||||
// Log any other error
|
||||
// Any other error is unexpected and we should try to
|
||||
// restart the watcher as a result to get into healthy
|
||||
// state again.
|
||||
else {
|
||||
this.error(msg, watcher);
|
||||
const handled = this.onUnexpectedError(msg, watcher);
|
||||
if (!handled) {
|
||||
this.error(`Unexpected error: ${msg} (ESHUTDOWN)`, watcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private onUnexpectedShutdown(watcher?: IWatcher): boolean {
|
||||
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 5 times already
|
||||
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 service shutdown unexpectedly and will be restarted', watcher);
|
||||
this.warn(`Watcher will be restarted due to unexpected error: ${error}`, watcher);
|
||||
this.restartWatching(watcher);
|
||||
|
||||
handled = true;
|
||||
|
@ -354,7 +341,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
}
|
||||
|
||||
private onWatchedPathDeleted(watcher: IWatcher): boolean {
|
||||
this.warn('Watcher service shutdown unexpectedly because watched path got deleted', watcher);
|
||||
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 }]);
|
||||
|
@ -368,7 +355,7 @@ export class NsfwWatcherService extends Disposable implements IWatcherService {
|
|||
|
||||
// Watcher path came back! Restart watching...
|
||||
if (path === watcher.request.path && (type === 'added' || type === 'changed')) {
|
||||
this.warn('Watcher service restarts for watched path got created again', watcher);
|
||||
this.warn('Watcher restarts because watched path got created again', watcher);
|
||||
|
||||
// Stop watching that parent folder
|
||||
disposable.dispose();
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import * as assert from 'assert';
|
||||
import { tmpdir } from 'os';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
import { Promises } from 'vs/base/node/pfs';
|
||||
|
@ -38,25 +39,31 @@ flakySuite('Recursive Watcher', () => {
|
|||
protected override getOptions(watcher: any) {
|
||||
return {
|
||||
...super.getOptions(watcher),
|
||||
|
||||
// required to let tests pass properly. with a smaller value
|
||||
// somehow `fsevents` on macOS seems to report events from
|
||||
// the past so that the event listening will assume wrongly
|
||||
debounceMS: 500
|
||||
debounceMS: 1
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let testDir: string;
|
||||
let service: TestNsfwWatcherService;
|
||||
let enableLogging = false;
|
||||
|
||||
let loggingEnabled = false;
|
||||
|
||||
function enableLogging(enable: boolean) {
|
||||
loggingEnabled = enable;
|
||||
service?.setVerboseLogging(enable);
|
||||
}
|
||||
|
||||
enableLogging(false);
|
||||
|
||||
setup(async () => {
|
||||
service = new TestNsfwWatcherService();
|
||||
|
||||
if (enableLogging) {
|
||||
service.onDidLogMessage(e => console.log(`[recursive watcher test message] ${e.message}`));
|
||||
}
|
||||
service.onDidLogMessage(e => {
|
||||
if (loggingEnabled) {
|
||||
console.log(`[recursive watcher test message] ${e.message}`);
|
||||
}
|
||||
});
|
||||
|
||||
testDir = getRandomTestPath(tmpdir(), 'vsctests', 'filewatcher');
|
||||
|
||||
|
@ -120,7 +127,7 @@ flakySuite('Recursive Watcher', () => {
|
|||
await Promises.rename(newFolderPath, renamedFolderPath);
|
||||
await changeFuture;
|
||||
|
||||
// TODO: case rename is currently broken on macOS (https://github.com/Axosoft/nsfw/issues/146)
|
||||
// Case rename is currently broken on macOS (https://github.com/Axosoft/nsfw/issues/146)
|
||||
if (isWindows) {
|
||||
|
||||
// Rename file (same name, different case)
|
||||
|
@ -175,6 +182,7 @@ flakySuite('Recursive Watcher', () => {
|
|||
await changeFuture;
|
||||
|
||||
// Change file
|
||||
await timeout(1100); // nsfw cannot distinguish a create from a change when time period from create to change is <1s
|
||||
changeFuture = awaitEvent(service, copiedFilepath, FileChangeType.UPDATED);
|
||||
await Promises.writeFile(copiedFilepath, 'Hello Change');
|
||||
await changeFuture;
|
||||
|
|
|
@ -10408,10 +10408,10 @@ vscode-nls-dev@^3.3.1:
|
|||
xml2js "^0.4.19"
|
||||
yargs "^13.2.4"
|
||||
|
||||
vscode-nsfw@2.1.4:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-2.1.4.tgz#8965413e41a7099e36813921d3b468c12d1b6e59"
|
||||
integrity sha512-EsYWpACwojUjLgJFPhtnk/omId+oyuHpctWQiCLblf53eOxzYC7HT30NljEigl/sFmuTZM0IEMrsonj3GKDYPw==
|
||||
vscode-nsfw@2.1.6:
|
||||
version "2.1.6"
|
||||
resolved "https://registry.yarnpkg.com/vscode-nsfw/-/vscode-nsfw-2.1.6.tgz#51148fad0e7ff3b72fe0b3500903ab24af00f308"
|
||||
integrity sha512-3RCPQ1/yBvwJuaUG6bJMOcmlRKuDHFn2xx04u8hWYOkNQODK7LncP5qXwU/NNN32/Vgp/cDy4P1o5HoGAHFOfg==
|
||||
dependencies:
|
||||
node-addon-api "*"
|
||||
|
||||
|
|
Loading…
Reference in a new issue