diff --git a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts index e2a0dc5db6b..cefca23c785 100644 --- a/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts +++ b/src/vs/workbench/api/browser/mainThreadFileSystemEventService.ts @@ -212,7 +212,7 @@ export class MainThreadFileSystemEventService implements MainThreadFileSystemEve this._listener.add(workingCopyFileService.onDidRunWorkingCopyFileOperation(e => this._proxy.$onDidRunFileOperation(e.operation, e.files))); } - async $watch(extensionId: string, session: number, resource: UriComponents, unvalidatedOpts: IWatchOptions): Promise { + async $watch(extensionId: string, session: number, resource: UriComponents, unvalidatedOpts: IWatchOptions, correlate: boolean): Promise { const uri = URI.revive(resource); const opts: IWatchOptions = { @@ -234,8 +234,7 @@ export class MainThreadFileSystemEventService implements MainThreadFileSystemEve } } - // Correlated file watching is taken as is (for now we only opt into correlating with proposed new file system watcher API with excludes) - const correlate = Array.isArray(unvalidatedOpts?.excludes) && unvalidatedOpts.excludes.length > 0; + // Correlated file watching is taken as is if (correlate) { this._logService.trace(`MainThreadFileSystemEventService#$watch(): request to start watching correlated (extension: ${extensionId}, path: ${uri.toString(true)}, recursive: ${opts.recursive}, session: ${session})`); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index e3914aabe1a..9d6fa52947d 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -28,7 +28,7 @@ import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { Extension, IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService'; import { ExtHostFileSystem } from 'vs/workbench/api/common/extHostFileSystem'; -import { ExtHostFileSystemEventService } from 'vs/workbench/api/common/extHostFileSystemEventService'; +import { ExtHostFileSystemEventService, FileSystemWatcherCreateOptions } from 'vs/workbench/api/common/extHostFileSystemEventService'; import { ExtHostLanguageFeatures } from 'vs/workbench/api/common/extHostLanguageFeatures'; import { ExtHostLanguages } from 'vs/workbench/api/common/extHostLanguages'; import { ExtHostMessageService } from 'vs/workbench/api/common/extHostMessageService'; @@ -947,17 +947,21 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostBulkEdits.applyWorkspaceEdit(edit, extension, metadata); }, createFileSystemWatcher: (pattern, optionsOrIgnoreCreate, ignoreChange?, ignoreDelete?): vscode.FileSystemWatcher => { - let options: vscode.FileSystemWatcherOptions | undefined = undefined; + let options: FileSystemWatcherCreateOptions | undefined = undefined; if (typeof optionsOrIgnoreCreate === 'boolean') { options = { ignoreCreateEvents: Boolean(optionsOrIgnoreCreate), ignoreChangeEvents: Boolean(ignoreChange), - ignoreDeleteEvents: Boolean(ignoreDelete) + ignoreDeleteEvents: Boolean(ignoreDelete), + correlate: false }; } else if (optionsOrIgnoreCreate) { checkProposedApiEnabled(extension, 'createFileSystemWatcher'); - options = optionsOrIgnoreCreate; + options = { + ...optionsOrIgnoreCreate, + correlate: true + }; } return extHostFileSystemEvent.createFileSystemWatcher(extHostWorkspace, extension, pattern, options); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d55956d771c..ba58ea650d2 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1351,7 +1351,7 @@ export interface MainThreadFileSystemShape extends IDisposable { } export interface MainThreadFileSystemEventServiceShape extends IDisposable { - $watch(extensionId: string, session: number, resource: UriComponents, opts: files.IWatchOptions): void; + $watch(extensionId: string, session: number, resource: UriComponents, opts: files.IWatchOptions, correlate: boolean): void; $unwatch(session: number): void; } diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index 808519cdcc8..a1c9a2bdaf4 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -18,7 +18,9 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import { Lazy } from 'vs/base/common/lazy'; -interface FileSystemWatcherCreateOptions { +export interface FileSystemWatcherCreateOptions { + readonly correlate: boolean; + readonly ignoreCreateEvents?: boolean; readonly ignoreChangeEvents?: boolean; readonly ignoreDeleteEvents?: boolean; @@ -71,9 +73,9 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { const excludeOutOfWorkspaceEvents = typeof globPattern === 'string'; // 1.84.x introduces new proposed API for a watcher to set exclude - // rules. When this is provided, we turn the file watcher into correlation + // rules. In these cases, we turn the file watcher into correlation // mode and ignore any event that does not match the correlation ID. - const excludeUncorrelatedEvents = Array.isArray(options?.excludes) && options.excludes.length > 0; + const excludeUncorrelatedEvents = options?.correlate; const subscription = dispatcher(events => { if (typeof events.session === 'number' && events.session !== this.session) { @@ -110,10 +112,10 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { } }); - this._disposable = Disposable.from(this.ensureWatching(mainContext, extension, globPattern, options), this._onDidCreate, this._onDidChange, this._onDidDelete, subscription); + this._disposable = Disposable.from(this.ensureWatching(mainContext, extension, globPattern, options, options?.correlate), this._onDidCreate, this._onDidChange, this._onDidDelete, subscription); } - private ensureWatching(mainContext: IMainContext, extension: IExtensionDescription, globPattern: string | IRelativePatternDto, options?: FileSystemWatcherCreateOptions): Disposable { + private ensureWatching(mainContext: IMainContext, extension: IExtensionDescription, globPattern: string | IRelativePatternDto, options: FileSystemWatcherCreateOptions | undefined, correlate: boolean | undefined): Disposable { const disposable = Disposable.from(); if (typeof globPattern === 'string') { @@ -127,7 +129,7 @@ class FileSystemWatcher implements vscode.FileSystemWatcher { recursive = true; // only watch recursively if pattern indicates the need for it } - proxy.$watch(extension.identifier.value, this.session, globPattern.baseUri, { recursive, excludes: options?.excludes ?? [] }); + proxy.$watch(extension.identifier.value, this.session, globPattern.baseUri, { recursive, excludes: options?.excludes ?? [] }, Boolean(correlate)); return Disposable.from({ dispose: () => proxy.$unwatch(this.session) }); } diff --git a/src/vs/workbench/api/test/browser/extHostFileSystemEventService.test.ts b/src/vs/workbench/api/test/browser/extHostFileSystemEventService.test.ts index 7c42f7e90ee..ba5e260f4d7 100644 --- a/src/vs/workbench/api/test/browser/extHostFileSystemEventService.test.ts +++ b/src/vs/workbench/api/test/browser/extHostFileSystemEventService.test.ts @@ -22,13 +22,13 @@ suite('ExtHostFileSystemEventService', () => { drain: undefined! }; - const watcher1 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher(undefined!, undefined!, '**/somethingInteresting', {}); + const watcher1 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher(undefined!, undefined!, '**/somethingInteresting', { correlate: false }); assert.strictEqual(watcher1.ignoreChangeEvents, false); assert.strictEqual(watcher1.ignoreCreateEvents, false); assert.strictEqual(watcher1.ignoreDeleteEvents, false); watcher1.dispose(); - const watcher2 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher(undefined!, undefined!, '**/somethingBoring', { ignoreCreateEvents: true, ignoreChangeEvents: true, ignoreDeleteEvents: true }); + const watcher2 = new ExtHostFileSystemEventService(protocol, new NullLogService(), undefined!).createFileSystemWatcher(undefined!, undefined!, '**/somethingBoring', { ignoreCreateEvents: true, ignoreChangeEvents: true, ignoreDeleteEvents: true, correlate: false }); assert.strictEqual(watcher2.ignoreChangeEvents, true); assert.strictEqual(watcher2.ignoreCreateEvents, true); assert.strictEqual(watcher2.ignoreDeleteEvents, true); diff --git a/src/vscode-dts/vscode.proposed.createFileSystemWatcher.d.ts b/src/vscode-dts/vscode.proposed.createFileSystemWatcher.d.ts index d12daf66271..8ef9b90a8ea 100644 --- a/src/vscode-dts/vscode.proposed.createFileSystemWatcher.d.ts +++ b/src/vscode-dts/vscode.proposed.createFileSystemWatcher.d.ts @@ -33,6 +33,19 @@ declare module 'vscode' { export namespace workspace { + /** + * A variant of {@link workspace.createFileSystemWatcher} that optionally allows to specify + * a set of glob patterns to exclude from watching. + * + * It provides the following advantages over the other {@link workspace.createFileSystemWatcher} + * method: + * - the configured excludes from `files.watcherExclude` setting are NOT applied + * - requests for recursive file watchers inside the opened workspace are NOT ignored + * - the watcher is ONLY notified for events from this request and not from any other watcher + * + * As such, this method is prefered in cases where you want full control over the watcher behavior + * without being impacted by settings or other watchers that are installed. + */ export function createFileSystemWatcher(pattern: RelativePattern, options?: FileSystemWatcherOptions): FileSystemWatcher; } }