From eb160fb65dc307e5882f2d220f5cecda1ee4d92e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 28 Oct 2021 09:59:03 +0200 Subject: [PATCH] watcher - enable parcel watcher by default --- build/gulpfile.reh.js | 4 - package.json | 2 - remote/package.json | 1 - remote/yarn.lock | 107 ----- .../test/node/nativeModules.test.ts | 13 +- .../files/node/diskFileSystemProvider.ts | 20 +- .../watcher/unix/chokidarWatcherService.ts | 374 ------------------ .../unix/test/chockidarWatcherService.test.ts | 96 ----- .../files/node/watcher/unix/watcher.ts | 26 -- .../files/node/watcher/unix/watcherApp.ts | 12 - .../files/node/watcher/unix/watcherService.ts | 98 ----- src/vs/workbench/buildfile.desktop.js | 1 - .../files/browser/files.contribution.ts | 2 +- yarn.lock | 34 -- 14 files changed, 5 insertions(+), 785 deletions(-) delete mode 100644 src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts delete mode 100644 src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts delete mode 100644 src/vs/platform/files/node/watcher/unix/watcher.ts delete mode 100644 src/vs/platform/files/node/watcher/unix/watcherApp.ts delete mode 100644 src/vs/platform/files/node/watcher/unix/watcherService.ts diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index 22818f0fa27..1334cd15d6e 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -102,10 +102,6 @@ const serverEntryPoints = [ name: 'vs/server/remoteExtensionHostProcess', exclude: ['vs/css', 'vs/nls'] }, - { - name: 'vs/platform/files/node/watcher/unix/watcherApp', - exclude: ['vs/css', 'vs/nls'] - }, { name: 'vs/platform/files/node/watcher/nsfw/watcherApp', exclude: ['vs/css', 'vs/nls'] diff --git a/package.json b/package.json index 6de3e12b5a4..1891ec4398f 100644 --- a/package.json +++ b/package.json @@ -63,7 +63,6 @@ "@vscode/sqlite3": "4.0.12", "@vscode/vscode-languagedetection": "1.0.21", "applicationinsights": "1.0.8", - "chokidar": "3.5.1", "graceful-fs": "4.2.8", "http-proxy-agent": "^2.1.0", "https-proxy-agent": "^2.2.3", @@ -97,7 +96,6 @@ "devDependencies": { "7zip": "0.0.6", "@types/applicationinsights": "0.20.0", - "@types/chokidar": "2.1.3", "@types/cookie": "^0.3.3", "@types/copy-webpack-plugin": "^6.0.3", "@types/cssnano": "^4.0.0", diff --git a/remote/package.json b/remote/package.json index 2bdc0a1ef03..2fdf409e395 100644 --- a/remote/package.json +++ b/remote/package.json @@ -7,7 +7,6 @@ "@parcel/watcher": "2.0.0", "@vscode/vscode-languagedetection": "1.0.21", "applicationinsights": "1.0.8", - "chokidar": "3.5.1", "cookie": "^0.4.0", "graceful-fs": "4.2.8", "http-proxy-agent": "^2.1.0", diff --git a/remote/yarn.lock b/remote/yarn.lock index 8f9ecea79e9..d1ee27b7961 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -127,14 +127,6 @@ agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" -anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - applicationinsights@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" @@ -144,11 +136,6 @@ applicationinsights@1.0.8: diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" -binary-extensions@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" - integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== - bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" @@ -156,33 +143,11 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= -chokidar@3.5.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.3.1" - cookie@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" @@ -260,13 +225,6 @@ file-uri-to-path@2: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-2.0.0.tgz#7b415aeba227d575851e0a5b0c640d7656403fba" integrity sha512-hjPFI8oE/2iQPVe4gbrJ73Pp+Xfub2+WI2LlXDbsaJBwT5wuMh35WNWVYYTpnz895shtwfyutMFLFywpQAFdLg== -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -276,11 +234,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fsevents@~2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.1.tgz#b209ab14c61012636c8863507edf7fb68cc54e9f" - integrity sha512-YR47Eg4hChJGAB1O3yEAOkGO+rlzutoICGqGo9EZ4lKWokzZRSyIW1QmTzqjtw8MJdj9srP869CuWw/hyzSiBw== - ftp@^0.3.10: version "0.3.10" resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" @@ -301,13 +254,6 @@ get-uri@^3.0.2: fs-extra "^8.1.0" ftp "^0.3.10" -glob-parent@~5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - graceful-fs@4.2.8: version "4.2.8" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" @@ -374,30 +320,6 @@ ip@^1.1.5: resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - isarray@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" @@ -479,26 +401,11 @@ node-pty@0.11.0-beta7: dependencies: nan "^2.14.0" -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= -picomatch@^2.0.4: - version "2.0.7" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" - integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== - -picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - proxy-from-env@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" @@ -514,13 +421,6 @@ readable-stream@1.1.x: isarray "0.0.1" string_decoder "~0.10.x" -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== - dependencies: - picomatch "^2.2.1" - semver@^5.3.0: version "5.6.0" resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" @@ -567,13 +467,6 @@ tas-client-umd@0.1.4: resolved "https://registry.yarnpkg.com/tas-client-umd/-/tas-client-umd-0.1.4.tgz#49db4130dd63a8342fabf77185a740fc6a7bea80" integrity sha512-1hFqJeLD3ryNikniIaO7TItlXhS5vx7bJ+wbPDf8o+IifgwwOWK2ARisdEM9SnJd0ccfcwNPG6Po+RiKn5L2hg== -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" diff --git a/src/vs/platform/environment/test/node/nativeModules.test.ts b/src/vs/platform/environment/test/node/nativeModules.test.ts index 3cb1ee1a509..130d0561679 100644 --- a/src/vs/platform/environment/test/node/nativeModules.test.ts +++ b/src/vs/platform/environment/test/node/nativeModules.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { isMacintosh, isWindows } from 'vs/base/common/platform'; +import { isWindows } from 'vs/base/common/platform'; function testErrorMessage(module: string): string { return `Unable to load "${module}" dependency. It was probably not compiled for the right operating system architecture or had missing build tools.`; @@ -53,17 +53,6 @@ suite('Native Modules (all platforms)', () => { }); }); -(!isMacintosh ? suite.skip : suite)('Native Modules (macOS)', () => { - - test('chokidar (fsevents)', async () => { - const chokidar = await import('chokidar'); - const watcher = chokidar.watch(__dirname); - assert.ok(watcher.options.useFsEvents, testErrorMessage('chokidar (fsevents)')); - - return watcher.close(); - }); -}); - (!isWindows ? suite.skip : suite)('Native Modules (Windows)', () => { test('windows-mutex', async () => { diff --git a/src/vs/platform/files/node/diskFileSystemProvider.ts b/src/vs/platform/files/node/diskFileSystemProvider.ts index 1c1a1284972..4cbb9ac740f 100644 --- a/src/vs/platform/files/node/diskFileSystemProvider.ts +++ b/src/vs/platform/files/node/diskFileSystemProvider.ts @@ -23,10 +23,8 @@ 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 { FileWatcher as UnixWatcherService } from 'vs/platform/files/node/watcher/unix/watcherService'; import { IDiskFileChange, ILogMessage, IWatchRequest, WatcherService } from 'vs/platform/files/common/watcher'; import { ILogService } from 'vs/platform/log/common/log'; -import product from 'vs/platform/product/common/product'; import { AbstractDiskFileSystemProvider } from 'vs/platform/files/common/diskFileSystemProvider'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -570,25 +568,13 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple let enableLegacyWatcher = false; if (this.options?.watcher?.usePolling) { - enableLegacyWatcher = false; // can use Parcel watcher for when polling is required + enableLegacyWatcher = false; // must use Parcel watcher for when polling is required } else { - if (this.options?.legacyWatcher === 'on' || this.options?.legacyWatcher === 'off') { - enableLegacyWatcher = this.options.legacyWatcher === 'on'; // setting always wins - } else { - if (product.quality === 'stable') { - // in stable use legacy for single folder workspaces - // TODO@bpasero remove me eventually - enableLegacyWatcher = folders === 1; - } - } + enableLegacyWatcher = this.options?.legacyWatcher === 'on'; // setting always wins } if (enableLegacyWatcher) { - if (isLinux) { - watcherImpl = UnixWatcherService; - } else { - watcherImpl = NsfwWatcherService; - } + watcherImpl = NsfwWatcherService; } else { watcherImpl = ParcelWatcherService; } diff --git a/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts deleted file mode 100644 index 495a823a0c6..00000000000 --- a/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts +++ /dev/null @@ -1,374 +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 chokidar from 'chokidar'; -import * as fs from 'fs'; -import * as gracefulFs from 'graceful-fs'; -import { equals } from 'vs/base/common/arrays'; -import { ThrottledDelayer } from 'vs/base/common/async'; -import { Emitter } from 'vs/base/common/event'; -import { isEqualOrParent } from 'vs/base/common/extpath'; -import { match, parse, ParsedPattern } from 'vs/base/common/glob'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { normalizeNFC } from 'vs/base/common/normalization'; -import { isLinux, isMacintosh } from 'vs/base/common/platform'; -import { realcaseSync } from 'vs/base/node/extpath'; -import { FileChangeType } from 'vs/platform/files/common/files'; -import { IWatcherOptions, IWatcherService } from 'vs/platform/files/node/watcher/unix/watcher'; -import { IDiskFileChange, ILogMessage, IWatchRequest, normalizeFileChanges } from 'vs/platform/files/common/watcher'; - -gracefulFs.gracefulify(fs); // enable gracefulFs - -process.noAsar = true; // disable ASAR support in watcher process - -interface IWatcher { - requests: ExtendedWatcherRequest[]; - stop(): Promise; -} - -interface ExtendedWatcherRequest extends IWatchRequest { - parsedPattern?: ParsedPattern; -} - -export class ChokidarWatcherService 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 EVENT_SPAM_WARNING_THRESHOLD = 60 * 1000; // warn after certain time span of event spam - - private readonly _onDidChangeFile = this._register(new Emitter()); - readonly onDidChangeFile = this._onDidChangeFile.event; - - private readonly _onDidLogMessage = this._register(new Emitter()); - readonly onDidLogMessage = this._onDidLogMessage.event; - - private watchers = new Map(); - - private _watcherCount = 0; - get wacherCount() { return this._watcherCount; } - - private pollingInterval?: number; - private usePolling?: boolean | string[]; - private verboseLogging: boolean | undefined; - - private spamCheckStartTime: number | undefined; - private spamWarningLogged: boolean | undefined; - private enospcErrorLogged: boolean | undefined; - - async init(options: IWatcherOptions): Promise { - this.pollingInterval = options.pollingInterval; - this.usePolling = options.usePolling; - this.watchers.clear(); - this._watcherCount = 0; - this.verboseLogging = options.verboseLogging; - } - - async setVerboseLogging(enabled: boolean): Promise { - this.verboseLogging = enabled; - } - - async watch(requests: IWatchRequest[]): Promise { - const watchers = new Map(); - const newRequests: string[] = []; - - const requestsByBasePath = normalizeRoots(requests); - - // evaluate new & remaining watchers - for (const basePath in requestsByBasePath) { - const watcher = this.watchers.get(basePath); - if (watcher && isEqualRequests(watcher.requests, requestsByBasePath[basePath])) { - watchers.set(basePath, watcher); - this.watchers.delete(basePath); - } else { - newRequests.push(basePath); - } - } - - // stop all old watchers - for (const [, watcher] of this.watchers) { - await watcher.stop(); - } - - // start all new watchers - for (const basePath of newRequests) { - const requests = requestsByBasePath[basePath]; - watchers.set(basePath, this.doWatch(basePath, requests)); - } - - this.watchers = watchers; - } - - private doWatch(basePath: string, requests: IWatchRequest[]): IWatcher { - const pollingInterval = this.pollingInterval || 5000; - let usePolling = this.usePolling; // boolean or a list of path patterns - if (Array.isArray(usePolling)) { - // switch to polling if one of the paths matches with a watched path - usePolling = usePolling.some(pattern => requests.some(request => match(pattern, request.path))); - } - - const watcherOpts: chokidar.WatchOptions = { - ignoreInitial: true, - ignorePermissionErrors: true, - followSymlinks: true, // this is the default of chokidar and supports file events through symlinks - interval: pollingInterval, // while not used in normal cases, if any error causes chokidar to fallback to polling, increase its intervals - binaryInterval: pollingInterval, - usePolling, - disableGlobbing: true // fix https://github.com/microsoft/vscode/issues/4586 - }; - - const excludes: string[] = []; - - const isSingleFolder = requests.length === 1; - if (isSingleFolder) { - excludes.push(...requests[0].excludes); // if there's only one request, use the built-in ignore-filterering - } - - if ((isMacintosh || isLinux) && (basePath.length === 0 || basePath === '/')) { - excludes.push('/dev/**'); - if (isLinux) { - excludes.push('/proc/**', '/sys/**'); - } - } - - excludes.push('**/*.asar'); // Ensure we never recurse into ASAR archives - - watcherOpts.ignored = excludes; - - // Chokidar fails when the basePath does not match case-identical to the path on disk - // so we have to find the real casing of the path and do some path massaging to fix this - // see https://github.com/paulmillr/chokidar/issues/418 - const realBasePath = isMacintosh ? (realcaseSync(basePath) || basePath) : basePath; - const realBasePathLength = realBasePath.length; - const realBasePathDiffers = (basePath !== realBasePath); - - if (realBasePathDiffers) { - this.warn(`Watcher basePath does not match version on disk and was corrected (original: ${basePath}, real: ${realBasePath})`); - } - - this.debug(`Start watching: ${realBasePath}, excludes: ${excludes.join(',')}, usePolling: ${usePolling ? 'true, interval ' + pollingInterval : 'false'}`); - - let chokidarWatcher: chokidar.FSWatcher | null = chokidar.watch(realBasePath, watcherOpts); - this._watcherCount++; - - // Detect if for some reason the native watcher library fails to load - if (isMacintosh && chokidarWatcher.options && !chokidarWatcher.options.useFsEvents) { - this.warn('Watcher is not using native fsevents library and is falling back to unefficient polling.'); - } - - let undeliveredFileEvents: IDiskFileChange[] = []; - let fileEventDelayer: ThrottledDelayer | null = new ThrottledDelayer(ChokidarWatcherService.FS_EVENT_DELAY); - - const watcher: IWatcher = { - requests, - stop: async () => { - try { - if (this.verboseLogging) { - this.log(`Stop watching: ${basePath}]`); - } - - if (chokidarWatcher) { - await chokidarWatcher.close(); - this._watcherCount--; - chokidarWatcher = null; - } - - if (fileEventDelayer) { - fileEventDelayer.cancel(); - fileEventDelayer = null; - } - } catch (error) { - this.warn('Error while stopping watcher: ' + error.toString()); - } - } - }; - - chokidarWatcher.on('all', (type: string, path: string) => { - if (isMacintosh) { - // Mac: uses NFD unicode form on disk, but we want NFC - // See also https://github.com/nodejs/node/issues/2165 - path = normalizeNFC(path); - } - - if (path.indexOf(realBasePath) < 0) { - return; // we really only care about absolute paths here in our basepath context here - } - - // Make sure to convert the path back to its original basePath form if the realpath is different - if (realBasePathDiffers) { - path = basePath + path.substr(realBasePathLength); - } - - let eventType: FileChangeType; - switch (type) { - case 'change': - eventType = FileChangeType.UPDATED; - break; - case 'add': - case 'addDir': - eventType = FileChangeType.ADDED; - break; - case 'unlink': - case 'unlinkDir': - eventType = FileChangeType.DELETED; - break; - default: - return; - } - - // if there's more than one request we need to do - // extra filtering due to potentially overlapping roots - if (!isSingleFolder) { - if (isIgnored(path, watcher.requests)) { - return; - } - } - - const event = { type: eventType, path }; - - // Logging - if (this.verboseLogging) { - this.log(`${eventType === FileChangeType.ADDED ? '[ADDED]' : eventType === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${path}`); - } - - // Check for spam - const now = Date.now(); - if (undeliveredFileEvents.length === 0) { - this.spamWarningLogged = false; - this.spamCheckStartTime = now; - } else if (!this.spamWarningLogged && typeof this.spamCheckStartTime === 'number' && this.spamCheckStartTime + ChokidarWatcherService.EVENT_SPAM_WARNING_THRESHOLD < now) { - this.spamWarningLogged = true; - this.warn(`Watcher is busy catching up with ${undeliveredFileEvents.length} file changes in 60 seconds. Latest changed path is "${event.path}"`); - } - - // Add to buffer - undeliveredFileEvents.push(event); - - if (fileEventDelayer) { - - // Delay and send buffer - fileEventDelayer.trigger(async () => { - const events = undeliveredFileEvents; - undeliveredFileEvents = []; - - // Broadcast to clients normalized - const normalizedEvents = normalizeFileChanges(events); - this._onDidChangeFile.fire(normalizedEvents); - - // Logging - if (this.verboseLogging) { - for (const e of normalizedEvents) { - this.log(` >> normalized ${e.type === FileChangeType.ADDED ? '[ADDED]' : e.type === FileChangeType.DELETED ? '[DELETED]' : '[CHANGED]'} ${e.path}`); - } - } - - return undefined; - }); - } - }); - - chokidarWatcher.on('error', (error: NodeJS.ErrnoException) => { - if (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 (error.code === 'ENOSPC') { - if (!this.enospcErrorLogged) { - this.enospcErrorLogged = true; - this.stop(); - this.error('Inotify limit reached (ENOSPC)'); - } - } else { - this.warn(error.toString()); - } - } - }); - return watcher; - } - - async stop(): Promise { - for (const [, watcher] of this.watchers) { - await watcher.stop(); - } - - this.watchers.clear(); - } - - private log(message: string) { - this._onDidLogMessage.fire({ type: 'trace', message: `[File Watcher (chokidar)] ` + message }); - } - - private debug(message: string) { - this._onDidLogMessage.fire({ type: 'debug', message: `[File Watcher (chokidar)] ` + message }); - } - - private warn(message: string) { - this._onDidLogMessage.fire({ type: 'warn', message: `[File Watcher (chokidar)] ` + message }); - } - - private error(message: string) { - this._onDidLogMessage.fire({ type: 'error', message: `[File Watcher (chokidar)] ` + message }); - } -} - -function isIgnored(path: string, requests: ExtendedWatcherRequest[]): boolean { - for (const request of requests) { - if (request.path === path) { - return false; - } - - if (isEqualOrParent(path, request.path)) { - if (!request.parsedPattern) { - if (request.excludes && request.excludes.length > 0) { - const pattern = `{${request.excludes.join(',')}}`; - request.parsedPattern = parse(pattern); - } else { - request.parsedPattern = () => false; - } - } - - const relPath = path.substr(request.path.length + 1); - if (!request.parsedPattern(relPath)) { - return false; - } - } - } - - return true; -} - -/** - * Normalizes a set of root paths by grouping by the most parent root path. - * equests with Sub paths are skipped if they have the same ignored set as the parent. - */ -export function normalizeRoots(requests: IWatchRequest[]): { [basePath: string]: IWatchRequest[] } { - requests = requests.sort((r1, r2) => r1.path.localeCompare(r2.path)); - - let prevRequest: IWatchRequest | null = null; - const result: { [basePath: string]: IWatchRequest[] } = Object.create(null); - for (const request of requests) { - const basePath = request.path; - const ignored = (request.excludes || []).sort(); - if (prevRequest && (isEqualOrParent(basePath, prevRequest.path))) { - if (!isEqualIgnore(ignored, prevRequest.excludes)) { - result[prevRequest.path].push({ path: basePath, excludes: ignored }); - } - } else { - prevRequest = { path: basePath, excludes: ignored }; - result[basePath] = [prevRequest]; - } - } - - return result; -} - -function isEqualRequests(r1: readonly IWatchRequest[], r2: readonly IWatchRequest[]) { - return equals(r1, r2, (a, b) => a.path === b.path && isEqualIgnore(a.excludes, b.excludes)); -} - -function isEqualIgnore(i1: readonly string[], i2: readonly string[]) { - return equals(i1, i2); -} diff --git a/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts b/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts deleted file mode 100644 index 73e42892f70..00000000000 --- a/src/vs/platform/files/node/watcher/unix/test/chockidarWatcherService.test.ts +++ /dev/null @@ -1,96 +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 assert from 'assert'; -import * as platform from 'vs/base/common/platform'; -import { IWatchRequest } from 'vs/platform/files/common/watcher'; - -suite('Chokidar normalizeRoots', async () => { - - // Load `chokidarWatcherService` within the suite to prevent all tests - // from failing to start if `chokidar` was not properly installed - const { normalizeRoots } = await import('vs/platform/files/node/watcher/unix/chokidarWatcherService'); - - function newRequest(basePath: string, ignored: string[] = []): IWatchRequest { - return { path: basePath, excludes: ignored }; - } - - function assertNormalizedRootPath(inputPaths: string[], expectedPaths: string[]) { - const requests = inputPaths.map(path => newRequest(path)); - const actual = normalizeRoots(requests); - assert.deepStrictEqual(Object.keys(actual).sort(), expectedPaths); - } - - function assertNormalizedRequests(inputRequests: IWatchRequest[], expectedRequests: { [path: string]: IWatchRequest[] }) { - const actual = normalizeRoots(inputRequests); - const actualPath = Object.keys(actual).sort(); - const expectedPaths = Object.keys(expectedRequests).sort(); - assert.deepStrictEqual(actualPath, expectedPaths); - for (let path of actualPath) { - let a = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path)); - let e = expectedRequests[path].sort((r1, r2) => r1.path.localeCompare(r2.path)); - assert.deepStrictEqual(a, e); - } - } - - test('should not impacts roots that don\'t overlap', () => { - if (platform.isWindows) { - assertNormalizedRootPath(['C:\\a'], ['C:\\a']); - assertNormalizedRootPath(['C:\\a', 'C:\\b'], ['C:\\a', 'C:\\b']); - assertNormalizedRootPath(['C:\\a', 'C:\\b', 'C:\\c\\d\\e'], ['C:\\a', 'C:\\b', 'C:\\c\\d\\e']); - } else { - assertNormalizedRootPath(['/a'], ['/a']); - assertNormalizedRootPath(['/a', '/b'], ['/a', '/b']); - assertNormalizedRootPath(['/a', '/b', '/c/d/e'], ['/a', '/b', '/c/d/e']); - } - }); - - test('should remove sub-folders of other roots', () => { - if (platform.isWindows) { - assertNormalizedRootPath(['C:\\a', 'C:\\a\\b'], ['C:\\a']); - assertNormalizedRootPath(['C:\\a', 'C:\\b', 'C:\\a\\b'], ['C:\\a', 'C:\\b']); - assertNormalizedRootPath(['C:\\b\\a', 'C:\\a', 'C:\\b', 'C:\\a\\b'], ['C:\\a', 'C:\\b']); - assertNormalizedRootPath(['C:\\a', 'C:\\a\\b', 'C:\\a\\c\\d'], ['C:\\a']); - } else { - assertNormalizedRootPath(['/a', '/a/b'], ['/a']); - assertNormalizedRootPath(['/a', '/b', '/a/b'], ['/a', '/b']); - assertNormalizedRootPath(['/b/a', '/a', '/b', '/a/b'], ['/a', '/b']); - assertNormalizedRootPath(['/a', '/a/b', '/a/c/d'], ['/a']); - assertNormalizedRootPath(['/a/c/d/e', '/a/b/d', '/a/c/d', '/a/c/e/f', '/a/b'], ['/a/b', '/a/c/d', '/a/c/e/f']); - } - }); - - test('should remove duplicates', () => { - if (platform.isWindows) { - assertNormalizedRootPath(['C:\\a', 'C:\\a\\', 'C:\\a'], ['C:\\a']); - } else { - assertNormalizedRootPath(['/a', '/a/', '/a'], ['/a']); - assertNormalizedRootPath(['/a', '/b', '/a/b'], ['/a', '/b']); - assertNormalizedRootPath(['/b/a', '/a', '/b', '/a/b'], ['/a', '/b']); - assertNormalizedRootPath(['/a', '/a/b', '/a/c/d'], ['/a']); - } - }); - - test('nested requests', () => { - let p1, p2, p3; - if (platform.isWindows) { - p1 = 'C:\\a'; - p2 = 'C:\\a\\b'; - p3 = 'C:\\a\\b\\c'; - } else { - p1 = '/a'; - p2 = '/a/b'; - p3 = '/a/b/c'; - } - const r1 = newRequest(p1, ['**/*.ts']); - const r2 = newRequest(p2, ['**/*.js']); - const r3 = newRequest(p3, ['**/*.ts']); - assertNormalizedRequests([r1, r2], { [p1]: [r1, r2] }); - assertNormalizedRequests([r2, r1], { [p1]: [r1, r2] }); - assertNormalizedRequests([r1, r2, r3], { [p1]: [r1, r2, r3] }); - assertNormalizedRequests([r1, r3], { [p1]: [r1] }); - assertNormalizedRequests([r2, r3], { [p2]: [r2, r3] }); - }); -}); diff --git a/src/vs/platform/files/node/watcher/unix/watcher.ts b/src/vs/platform/files/node/watcher/unix/watcher.ts deleted file mode 100644 index d8b67fb998a..00000000000 --- a/src/vs/platform/files/node/watcher/unix/watcher.ts +++ /dev/null @@ -1,26 +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 IWatcherOptions { - pollingInterval?: number; - usePolling?: boolean | string[]; // boolean or a set of glob patterns matching folders that need polling - verboseLogging?: boolean; -} - -export interface IWatcherService { - - readonly onDidChangeFile: Event; - readonly onDidLogMessage: Event; - - init(options: IWatcherOptions): Promise; - - watch(paths: IWatchRequest[]): Promise; - setVerboseLogging(enabled: boolean): Promise; - - stop(): Promise; -} diff --git a/src/vs/platform/files/node/watcher/unix/watcherApp.ts b/src/vs/platform/files/node/watcher/unix/watcherApp.ts deleted file mode 100644 index c56a3bbc40f..00000000000 --- a/src/vs/platform/files/node/watcher/unix/watcherApp.ts +++ /dev/null @@ -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 { ChokidarWatcherService } from 'vs/platform/files/node/watcher/unix/chokidarWatcherService'; - -const server = new Server('watcher'); -const service = new ChokidarWatcherService(); -server.registerChannel('watcher', ProxyChannel.fromService(service)); diff --git a/src/vs/platform/files/node/watcher/unix/watcherService.ts b/src/vs/platform/files/node/watcher/unix/watcherService.ts deleted file mode 100644 index 9ce026a3f52..00000000000 --- a/src/vs/platform/files/node/watcher/unix/watcherService.ts +++ /dev/null @@ -1,98 +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 { IWatcherOptions, IWatcherService } from 'vs/platform/files/node/watcher/unix/watcher'; -import { IDiskFileChange, ILogMessage, IWatchRequest, WatcherService } from 'vs/platform/files/common/watcher'; - -/** - * @deprecated - */ -export class FileWatcher extends WatcherService { - - private static readonly MAX_RESTARTS = 5; - - private service: IWatcherService | undefined; - - private isDisposed = false; - private restartCounter = 0; - - private requests: IWatchRequest[] | undefined = undefined; - - constructor( - private readonly onDidFilesChange: (changes: IDiskFileChange[]) => void, - private readonly onLogMessage: (msg: ILogMessage) => void, - private verboseLogging: boolean, - private readonly watcherOptions: IWatcherOptions = {} - ) { - super(); - - this.startWatching(); - } - - private startWatching(): void { - const client = this._register(new Client( - FileAccess.asFileUri('bootstrap-fork', require).fsPath, - { - serverName: 'File Watcher (chokidar)', - args: ['--type=watcherServiceChokidar'], - env: { - VSCODE_AMD_ENTRYPOINT: 'vs/platform/files/node/watcher/unix/watcherApp', - VSCODE_PIPE_LOGGING: 'true', - VSCODE_VERBOSE_LOGGING: 'true' // transmit console logs from server to client - } - } - )); - - this._register(client.onDidProcessExit(() => { - // our watcher app should never be completed because it keeps on watching. being in here indicates - // that the watcher process died and we want to restart it here. we only do it a max number of times - if (!this.isDisposed) { - if (this.restartCounter <= FileWatcher.MAX_RESTARTS && this.requests) { - this.error('terminated unexpectedly and is restarted again...'); - this.restartCounter++; - this.startWatching(); - this.service?.watch(this.requests); - } else { - this.error('failed to start after retrying for some time, giving up. Please report this as a bug report!'); - } - } - })); - - // Initialize watcher - this.service = ProxyChannel.toService(getNextTickChannel(client.getChannel('watcher'))); - this.service.init({ ...this.watcherOptions, verboseLogging: 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 { - this.verboseLogging = verboseLogging; - - if (!this.isDisposed) { - await this.service?.setVerboseLogging(verboseLogging); - } - } - - error(message: string) { - this.onLogMessage({ type: 'error', message: `[File Watcher (chokidar)] ${message}` }); - } - - async watch(requests: IWatchRequest[]): Promise { - this.requests = requests; - - await this.service?.watch(requests); - } - - override dispose(): void { - this.isDisposed = true; - - super.dispose(); - } -} diff --git a/src/vs/workbench/buildfile.desktop.js b/src/vs/workbench/buildfile.desktop.js index 463074b2cfe..60953cc2ff1 100644 --- a/src/vs/workbench/buildfile.desktop.js +++ b/src/vs/workbench/buildfile.desktop.js @@ -14,7 +14,6 @@ exports.collectModules = function () { createModuleDescription('vs/workbench/services/search/node/searchApp'), - createModuleDescription('vs/platform/files/node/watcher/unix/watcherApp'), createModuleDescription('vs/platform/files/node/watcher/nsfw/watcherApp'), createModuleDescription('vs/platform/files/node/watcher/parcel/watcherApp'), diff --git a/src/vs/workbench/contrib/files/browser/files.contribution.ts b/src/vs/workbench/contrib/files/browser/files.contribution.ts index 93a7e0503e5..efc9a5a26d3 100644 --- a/src/vs/workbench/contrib/files/browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/files.contribution.ts @@ -266,7 +266,7 @@ configurationRegistry.registerConfiguration({ '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 if you are using insiders version or whenever you open multi-root workspaces."), + 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."), diff --git a/yarn.lock b/yarn.lock index df4062c8415..89880e8f299 100644 --- a/yarn.lock +++ b/yarn.lock @@ -492,13 +492,6 @@ dependencies: applicationinsights "*" -"@types/chokidar@2.1.3": - version "2.1.3" - resolved "https://registry.yarnpkg.com/@types/chokidar/-/chokidar-2.1.3.tgz#123ab795dba6d89be04bf076e6aecaf8620db674" - integrity sha512-6qK3xoLLAhQVTucQGHTySwOVA1crHRXnJeLwqK6KIFkkKa2aoMFXh+WEi8PotxDtvN6MQJLyYN9ag9P6NLV81w== - dependencies: - chokidar "*" - "@types/color-name@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" @@ -2163,21 +2156,6 @@ charenc@~0.0.1: resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= -chokidar@*: - version "3.2.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.2.3.tgz#b9270a565d14f02f6bfdd537a6a2bbf5549b8c8c" - integrity sha512-GtrxGuRf6bzHQmXWRepvsGnXpkQkVU+D2/9a7dAe4a7v1NhrfZOZ2oKf76M3nOs46fFYL8D+Q8JYA4GYeJ8Cjw== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.2.0" - optionalDependencies: - fsevents "~2.1.1" - chokidar@3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.4.3.tgz#c1df38231448e45ca4ac588e6c79573ba6a57d5b" @@ -4358,11 +4336,6 @@ fsevents@^1.2.7: bindings "^1.5.0" nan "^2.12.1" -fsevents@~2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.1.tgz#74c64e21df71721845d0c44fe54b7f56b82995a9" - integrity sha512-4FRPXWETxtigtJW/gxzEDsX1LVbPAM93VleB83kZB+ellqbHMkyt2aJfuzNLRvFPnGi6bcE5SvfxgbXPeKteJw== - fsevents@~2.1.2: version "2.1.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" @@ -8405,13 +8378,6 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" -readdirp@~3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.2.0.tgz#c30c33352b12c96dfb4b895421a49fd5a9593839" - integrity sha512-crk4Qu3pmXwgxdSgGhgA/eXiJAPQiX4GMOZZMXnqKxHX7TaoL+3gQVo/WeuAiogr07DpnfjIMpXXa+PAIvwPGQ== - dependencies: - picomatch "^2.0.4" - readdirp@~3.5.0: version "3.5.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e"