diff --git a/src/vs/workbench/services/files/node/diskFileSystemProvider.ts b/src/vs/workbench/services/files/node/diskFileSystemProvider.ts index 530f3ea5c235d39148061e75f04707e475cc51ad..a645ac84ef03cb8c07b7c6aac2a745e1112cac20 100644 --- a/src/vs/workbench/services/files/node/diskFileSystemProvider.ts +++ b/src/vs/workbench/services/files/node/diskFileSystemProvider.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { mkdir, open, close, read, write, fdatasync } from 'fs'; +import * as os from 'os'; import { promisify } from 'util'; import { IDisposable, Disposable, toDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle'; import { IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, FileSystemProviderErrorCode, createFileSystemProviderError, FileSystemProviderError } from 'vs/platform/files/common/files'; @@ -408,22 +409,30 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro folders: { path: string, excludes: string[] }[], onChange: (changes: IDiskFileChange[]) => void, onLogMessage: (msg: ILogMessage) => void, - verboseLogging: boolean + verboseLogging: boolean, + watcherOptions?: { [key: string]: boolean | number | string } ): WindowsWatcherService | UnixWatcherService | NsfwWatcherService }; - - // Single Folder Watcher - if (this.recursiveFoldersToWatch.length === 1) { - if (isWindows) { - watcherImpl = WindowsWatcherService; - } else { - watcherImpl = UnixWatcherService; + let watcherOptions = undefined; + + if (this.forcePolling()) { + // WSL needs a polling watcher + watcherImpl = UnixWatcherService; + watcherOptions = { usePolling: true }; + } else { + // Single Folder Watcher + if (this.recursiveFoldersToWatch.length === 1) { + if (isWindows) { + watcherImpl = WindowsWatcherService; + } else { + watcherImpl = UnixWatcherService; + } } - } - // Multi Folder Watcher - else { - watcherImpl = NsfwWatcherService; + // Multi Folder Watcher + else { + watcherImpl = NsfwWatcherService; + } } // Create and start watching @@ -436,7 +445,8 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro } this.logService[msg.type](msg.message); }, - this.logService.getLevel() === LogLevel.Trace + this.logService.getLevel() === LogLevel.Trace, + watcherOptions ); if (!this.recursiveWatcherLogLevelListener) { @@ -504,6 +514,12 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro return createFileSystemProviderError(error, code); } + + forcePolling(): boolean { + // wsl1 needs polling + return isLinux && /^[\.\-0-9]+-Microsoft/.test(os.release()); + } + //#endregion dispose(): void { diff --git a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts index db31180b5a1b9911d9899548be1884b5b49124a3..2302d222f57961698705c26d3b9d4b35ccbebf04 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/chokidarWatcherService.ts @@ -23,10 +23,6 @@ interface IWatcher { stop(): any; } -export interface IChockidarWatcherOptions { - pollingInterval?: number; -} - interface ExtendedWatcherRequest extends IWatcherRequest { parsedPattern?: glob.ParsedPattern; } @@ -40,6 +36,7 @@ export class ChokidarWatcherService implements IWatcherService { private _watcherCount: number; private _pollingInterval?: number; + private _usePolling?: boolean; private _verboseLogging: boolean; private spamCheckStartTime: number; @@ -52,8 +49,9 @@ export class ChokidarWatcherService implements IWatcherService { private _onLogMessage = new Emitter(); readonly onLogMessage: Event = this._onLogMessage.event; - public watch(options: IWatcherOptions & IChockidarWatcherOptions): Event { + public watch(options: IWatcherOptions): Event { this._pollingInterval = options.pollingInterval; + this._usePolling = options.usePolling; this._watchers = Object.create(null); this._watcherCount = 0; return this.onWatchEvent; @@ -106,6 +104,10 @@ export class ChokidarWatcherService implements IWatcherService { } const pollingInterval = this._pollingInterval || 1000; + const usePolling = this._usePolling; + if (usePolling && this._verboseLogging) { + this.log(`Use polling instead of fs.watch: Polling interval ${pollingInterval} ms`); + } const watcherOpts: chokidar.IOptions = { ignoreInitial: true, @@ -113,6 +115,7 @@ export class ChokidarWatcherService implements IWatcherService { 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: usePolling, disableGlobbing: true // fix https://github.com/Microsoft/vscode/issues/4586 }; diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcher.ts b/src/vs/workbench/services/files/node/watcher/unix/watcher.ts index fa5d4b23335b086d6af98333ce3b0df140a44df7..fc87e15803a33e880dca077d9027af9bce0a28de 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcher.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcher.ts @@ -12,6 +12,8 @@ export interface IWatcherRequest { } export interface IWatcherOptions { + pollingInterval?: number; + usePolling?: boolean; } export interface IWatcherService { diff --git a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts index 887932e9e714fda9d62f06260877a89cab63ad7b..202b505d53a54df279d2eef0860677649ed2104e 100644 --- a/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts +++ b/src/vs/workbench/services/files/node/watcher/unix/watcherService.ts @@ -8,7 +8,7 @@ import { Client } from 'vs/base/parts/ipc/node/ipc.cp'; import { IDiskFileChange, ILogMessage } from 'vs/workbench/services/files/node/watcher/watcher'; import { WatcherChannelClient } from 'vs/workbench/services/files/node/watcher/unix/watcherIpc'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IWatcherRequest } from 'vs/workbench/services/files/node/watcher/unix/watcher'; +import { IWatcherRequest, IWatcherOptions } from 'vs/workbench/services/files/node/watcher/unix/watcher'; import { getPathFromAmdModule } from 'vs/base/common/amd'; export class FileWatcher extends Disposable { @@ -22,7 +22,8 @@ export class FileWatcher extends Disposable { private folders: IWatcherRequest[], private onFileChanges: (changes: IDiskFileChange[]) => void, private onLogMessage: (msg: ILogMessage) => void, - private verboseLogging: boolean + private verboseLogging: boolean, + private watcherOptions: IWatcherOptions = {} ) { super(); @@ -66,8 +67,7 @@ export class FileWatcher extends Disposable { this.service.setVerboseLogging(this.verboseLogging); - const options = {}; - this._register(this.service.watch(options)(e => !this.isDisposed && this.onFileChanges(e))); + this._register(this.service.watch(this.watcherOptions)(e => !this.isDisposed && this.onFileChanges(e))); this._register(this.service.onLogMessage(m => this.onLogMessage(m)));