提交 11d42b6e 编写于 作者: B Benjamin Pasero

files2 - scaffold watch() that returns a disposable

上级 f5391c16
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { onUnexpectedError } from 'vs/base/common/errors';
import { ConfigurationModelParser, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { ConfigWatcher } from 'vs/base/node/config';
......@@ -70,8 +70,7 @@ export class FileServiceBasedUserConfiguration extends Disposable {
this._register(fileService.onFileChanges(e => this.handleFileEvents(e)));
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
this.fileService.watch(this.configurationResource);
this._register(toDisposable(() => this.fileService.unwatch(this.configurationResource)));
this._register(this.fileService.watch(this.configurationResource));
}
initialize(): Promise<ConfigurationModel> {
......
......@@ -164,14 +164,9 @@ export interface IFileService {
del(resource: URI, options?: { useTrash?: boolean, recursive?: boolean }): Promise<void>;
/**
* Allows to start a watcher that reports file change events on the provided resource.
* Allows to start a watcher that reports file/folder change events on the provided resource.
*/
watch(resource: URI): void;
/**
* Allows to stop a watcher on the provided resource or absolute fs path.
*/
unwatch(resource: URI): void;
watch(resource: URI, opts?: IWatchOptions): IDisposable;
/**
* Frees up any resources occupied by this service.
......@@ -1135,8 +1130,4 @@ export interface ILegacyFileService {
updateContent(resource: URI, value: string | ITextSnapshot, options?: IUpdateContentOptions): Promise<IFileStat>;
createFile(resource: URI, content?: string, options?: ICreateFileOptions): Promise<IFileStat>;
watch(resource: URI): void;
unwatch(resource: URI): void;
}
\ No newline at end of file
......@@ -802,9 +802,7 @@ export class SimpleRemoteFileService implements IFileService {
del(_resource: URI, _options?: { useTrash?: boolean, recursive?: boolean }): Promise<void> { return Promise.resolve(); }
watch(_resource: URI): void { }
unwatch(_resource: URI): void { }
watch(_resource: URI): IDisposable { return Disposable.None; }
getWriteEncoding(_resource: URI): IResourceEncoding { return { encoding: 'utf8', hasBOM: false }; }
......
......@@ -12,7 +12,7 @@ import { ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/te
import { FileOperationEvent, FileOperation, IFileService, FileChangeType, FileChangesEvent } from 'vs/platform/files/common/files';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { distinct, coalesce } from 'vs/base/common/arrays';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
......@@ -31,10 +31,9 @@ import { withNullAsUndefined } from 'vs/base/common/types';
export class FileEditorTracker extends Disposable implements IWorkbenchContribution {
protected closeOnFileDelete: boolean;
private modelLoadQueue: ResourceQueue;
private activeOutOfWorkspaceWatchers: ResourceMap<URI>;
private closeOnFileDelete: boolean;
private modelLoadQueue = new ResourceQueue();
private activeOutOfWorkspaceWatchers = new Map<URI, IDisposable>();
constructor(
@IEditorService private readonly editorService: IEditorService,
......@@ -49,9 +48,6 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
) {
super();
this.modelLoadQueue = new ResourceQueue();
this.activeOutOfWorkspaceWatchers = new ResourceMap<URI>();
this.onConfigurationUpdated(configurationService.getValue<IWorkbenchEditorConfiguration>());
this.registerListeners();
......@@ -350,9 +346,9 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
});
// Handle no longer visible out of workspace resources
this.activeOutOfWorkspaceWatchers.forEach(resource => {
this.activeOutOfWorkspaceWatchers.forEach((disposable, resource) => {
if (!visibleOutOfWorkspacePaths.get(resource)) {
this.fileService.unwatch(resource);
dispose(disposable);
this.activeOutOfWorkspaceWatchers.delete(resource);
}
});
......@@ -360,8 +356,8 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
// Handle newly visible out of workspace resources
visibleOutOfWorkspacePaths.forEach(resource => {
if (!this.activeOutOfWorkspaceWatchers.get(resource)) {
this.fileService.watch(resource);
this.activeOutOfWorkspaceWatchers.set(resource, resource);
const disposable = this.fileService.watch(resource);
this.activeOutOfWorkspaceWatchers.set(resource, disposable);
}
});
}
......@@ -369,8 +365,8 @@ export class FileEditorTracker extends Disposable implements IWorkbenchContribut
dispose(): void {
super.dispose();
// Dispose watchers if any
this.activeOutOfWorkspaceWatchers.forEach(resource => this.fileService.unwatch(resource));
// Dispose remaining watchers if any
this.activeOutOfWorkspaceWatchers.forEach(disposable => dispose(disposable));
this.activeOutOfWorkspaceWatchers.clear();
}
}
......@@ -114,20 +114,16 @@ namespace snippetExt {
}
function watch(service: IFileService, resource: URI, callback: (type: FileChangeType, resource: URI) => any): IDisposable {
let listener = service.onFileChanges(e => {
for (const change of e.changes) {
if (resources.isEqualOrParent(change.resource, resource)) {
callback(change.type, change.resource);
return combinedDisposable([
service.watch(resource),
service.onFileChanges(e => {
for (const change of e.changes) {
if (resources.isEqualOrParent(change.resource, resource)) {
callback(change.type, change.resource);
}
}
}
});
service.watch(resource);
return {
dispose() {
listener.dispose();
service.unwatch(resource);
}
};
})
]);
}
class SnippetsService implements ISnippetsService {
......
......@@ -10,7 +10,7 @@ import { Event, Emitter } from 'vs/base/common/event';
import * as pfs from 'vs/base/node/pfs';
import * as errors from 'vs/base/common/errors';
import * as collections from 'vs/base/common/collections';
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { RunOnceScheduler, Delayer } from 'vs/base/common/async';
import { FileChangeType, FileChangesEvent, IContent, IFileService } from 'vs/platform/files/common/files';
import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
......@@ -355,6 +355,8 @@ class NodeBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration {
class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfiguration {
private workspaceConfig: URI | null = null;
private workspaceConfigWatcher: IDisposable;
private readonly reloadConfigurationScheduler: RunOnceScheduler;
constructor(private fileService: IFileService, from?: IWorkspaceConfiguration) {
......@@ -362,27 +364,22 @@ class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfigurat
this.workspaceConfig = from && from.workspaceIdentifier ? from.workspaceIdentifier.configPath : null;
this._register(fileService.onFileChanges(e => this.handleWorkspaceFileEvents(e)));
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this._onDidChange.fire(), 50));
this.watchWorkspaceConfigurationFile();
this._register(toDisposable(() => this.unWatchWorkspaceConfigurtionFile()));
this.workspaceConfigWatcher = this.watchWorkspaceConfigurationFile();
}
private watchWorkspaceConfigurationFile(): void {
private watchWorkspaceConfigurationFile(): IDisposable {
if (this.workspaceConfig) {
this.fileService.watch(this.workspaceConfig);
return this.fileService.watch(this.workspaceConfig);
}
}
private unWatchWorkspaceConfigurtionFile(): void {
if (this.workspaceConfig) {
this.fileService.unwatch(this.workspaceConfig);
}
return Disposable.None;
}
protected loadWorkspaceConfigurationContents(workspaceIdentifier: IWorkspaceIdentifier): Promise<string> {
if (!(this.workspaceConfig && resources.isEqual(this.workspaceConfig, workspaceIdentifier.configPath))) {
this.unWatchWorkspaceConfigurtionFile();
this.workspaceConfigWatcher = dispose(this.workspaceConfigWatcher);
this.workspaceConfig = workspaceIdentifier.configPath;
this.watchWorkspaceConfigurationFile();
this.workspaceConfigWatcher = this.watchWorkspaceConfigurationFile();
}
return this.fileService.resolveContent(this.workspaceConfig)
.then(content => content.value, e => {
......@@ -406,6 +403,12 @@ class FileServiceBasedWorkspaceConfiguration extends AbstractWorkspaceConfigurat
}
}
}
dispose(): void {
super.dispose();
this.workspaceConfigWatcher = dispose(this.workspaceConfigWatcher);
}
}
class CachedWorkspaceConfiguration extends Disposable implements IWorkspaceConfiguration {
......
......@@ -1002,7 +1002,7 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
});
}
watch(resource: uri): void {
watch(resource: uri): IDisposable {
assert.ok(resource && resource.scheme === Schemas.file, `Invalid resource for watching: ${resource}`);
// Check for existing watcher first
......@@ -1010,7 +1010,7 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
if (entry) {
entry.count += 1;
return;
return Disposable.None;
}
// Create or get watcher for provided path
......@@ -1066,6 +1066,8 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
count: 1,
unwatch: () => watcherDisposable.dispose()
});
return watcherDisposable;
}
private onRawFileChange(event: IRawFileChange): void {
......@@ -1119,13 +1121,6 @@ export class FileService extends Disposable implements ILegacyFileService, IFile
this.activeFileChangesWatchers.clear();
}
// Tests only
resolve(resource: uri, options?: IResolveFileOptions): Promise<IFileStat>;
......
......@@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IDisposable, Disposable, dispose } from 'vs/base/common/lifecycle';
import { Schemas } from 'vs/base/common/network';
import * as resources from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
......@@ -12,7 +12,7 @@ import { ITextResourceConfigurationService } from 'vs/editor/common/services/res
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileWriteOptions, FileSystemProviderCapabilities, IContent, ICreateFileOptions, IFileSystemProvider, IFilesConfiguration, IResolveContentOptions, IStreamContent, ITextSnapshot, IUpdateContentOptions, StringSnapshot, IWatchOptions, ILegacyFileService, IFileService, toFileOperationResult, IFileStatWithMetadata } from 'vs/platform/files/common/files';
import { FileOperation, FileOperationError, FileOperationEvent, FileOperationResult, FileWriteOptions, FileSystemProviderCapabilities, IContent, ICreateFileOptions, IFileSystemProvider, IFilesConfiguration, IResolveContentOptions, IStreamContent, ITextSnapshot, IUpdateContentOptions, StringSnapshot, ILegacyFileService, IFileService, toFileOperationResult, IFileStatWithMetadata } from 'vs/platform/files/common/files';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IStorageService } from 'vs/platform/storage/common/storage';
......@@ -23,10 +23,10 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
class WorkspaceWatchLogic extends Disposable {
private _watches = new Map<string, URI>();
private _watches = new Map<string, IDisposable>();
constructor(
private _fileService: RemoteFileService,
private _fileService: IFileService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IWorkspaceContextService private readonly _contextService: IWorkspaceContextService,
) {
......@@ -76,19 +76,19 @@ class WorkspaceWatchLogic extends Disposable {
}
}
}
this._watches.set(resource.toString(), resource);
this._fileService.watch(resource, { recursive: true, excludes });
const disposable = this._fileService.watch(resource, { recursive: true, excludes });
this._watches.set(resource.toString(), disposable);
}
private _unwatchWorkspace(resource: URI) {
if (this._watches.has(resource.toString())) {
this._fileService.unwatch(resource);
dispose(this._watches.get(resource.toString()));
this._watches.delete(resource.toString());
}
}
private _unwatchWorkspaces() {
this._watches.forEach(uri => this._fileService.unwatch(uri));
this._watches.forEach(disposable => dispose(disposable));
this._watches.clear();
}
}
......@@ -309,41 +309,6 @@ export class RemoteFileService extends FileService {
content.value.on('end', () => resolve(result));
});
}
private _activeWatches = new Map<string, { unwatch: Promise<IDisposable>, count: number }>();
watch(resource: URI, opts: IWatchOptions = { recursive: false, excludes: [] }): void {
if (resource.scheme === Schemas.file) {
return super.watch(resource);
}
const key = resource.toString();
const entry = this._activeWatches.get(key);
if (entry) {
entry.count += 1;
return;
}
this._activeWatches.set(key, {
count: 1,
unwatch: this._withProvider(resource).then(provider => {
return provider.watch(resource, opts);
}, _err => {
return { dispose() { } };
})
});
}
unwatch(resource: URI): void {
if (resource.scheme === Schemas.file) {
return super.unwatch(resource);
}
let entry = this._activeWatches.get(resource.toString());
if (entry && --entry.count === 0) {
entry.unwatch.then(dispose);
this._activeWatches.delete(resource.toString());
}
}
}
registerSingleton(ILegacyFileService, RemoteFileService);
\ No newline at end of file
......@@ -3,8 +3,8 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { IFileService, IResolveFileOptions, IResourceEncodings, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, IResolveContentOptions, IContent, IStreamContent, ITextSnapshot, IUpdateContentOptions, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata } from 'vs/platform/files/common/files';
import { Disposable, IDisposable, toDisposable, combinedDisposable, dispose } from 'vs/base/common/lifecycle';
import { IFileService, IResolveFileOptions, IResourceEncodings, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, IResolveContentOptions, IContent, IStreamContent, ITextSnapshot, IUpdateContentOptions, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { Event, Emitter } from 'vs/base/common/event';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
......@@ -20,6 +20,8 @@ export class FileService2 extends Disposable implements IFileService {
//#region TODO@Ben HACKS
private _legacy: IFileService | null;
private joinOnLegacy: Promise<IFileService>;
private joinOnImplResolve: (service: IFileService) => void;
setLegacyService(legacy: IFileService): void {
this._legacy = this._register(legacy);
......@@ -38,9 +40,6 @@ export class FileService2 extends Disposable implements IFileService {
_serviceBrand: ServiceIdentifier<any>;
private joinOnLegacy: Promise<IFileService>;
private joinOnImplResolve: (service: IFileService) => void;
constructor(@ILogService private logService: ILogService) {
super();
......@@ -586,12 +585,58 @@ export class FileService2 extends Disposable implements IFileService {
private _onFileChanges: Emitter<FileChangesEvent> = this._register(new Emitter<FileChangesEvent>());
get onFileChanges(): Event<FileChangesEvent> { return this._onFileChanges.event; }
watch(resource: URI): void {
this.joinOnLegacy.then(legacy => legacy.watch(resource));
private activeWatches = new Map<string, { disposable: IDisposable, count: number }>();
watch(resource: URI, options: IWatchOptions = { recursive: false, excludes: [] }): IDisposable {
let watchDisposable: IDisposable = Disposable.None;
// Watch and wire in disposable which is async
this.doWatch(resource, options).then(disposable => watchDisposable = disposable);
return toDisposable(() => watchDisposable.dispose());
}
unwatch(resource: URI): void {
this.joinOnLegacy.then(legacy => legacy.unwatch(resource));
async doWatch(resource: URI, options: IWatchOptions): Promise<IDisposable> {
const provider = await this.withProvider(resource);
const key = this.toWatchKey(provider, resource, options);
// Only start watching if we are the first for the given key
const entry = this.activeWatches.get(key) || { count: 0, disposable: provider.watch(resource, options) };
if (!this.activeWatches.has(key)) {
this.activeWatches.set(key, entry);
}
// Increment usage counter
entry.count += 1;
return toDisposable(() => {
// Unref
entry.count--;
// Dispose only when last user is reached
if (entry.count === 0) {
dispose(entry.disposable);
this.activeWatches.delete(key);
}
});
}
private toWatchKey(provider: IFileSystemProvider, resource: URI, options: IWatchOptions): string {
const isPathCaseSensitive = !!(provider.capabilities & FileSystemProviderCapabilities.PathCaseSensitive);
return [
isPathCaseSensitive ? resource.toString() : resource.toString().toLowerCase(), // lowercase path is the provider is case insensitive
String(options.recursive), // use recursive: true | false as part of the key
options.excludes.join() // use excludes as part of the key
].join();
}
dispose(): void {
super.dispose();
this.activeWatches.forEach(watcher => dispose(watcher.disposable));
this.activeWatches.clear();
}
//#endregion
......
......@@ -7,9 +7,10 @@ import * as assert from 'assert';
import { FileService2 } from 'vs/workbench/services/files2/common/fileService2';
import { URI } from 'vs/base/common/uri';
import { IFileSystemProviderRegistrationEvent, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { NullFileSystemProvider } from 'vs/workbench/test/workbenchTestServices';
import { NullLogService } from 'vs/platform/log/common/log';
import { timeout } from 'vs/base/common/async';
suite('File Service 2', () => {
......@@ -61,4 +62,51 @@ suite('File Service 2', () => {
assert.equal(registrations[1].scheme, 'test');
assert.equal(registrations[1].added, false);
});
test('watch', async () => {
const service = new FileService2(new NullLogService());
let disposeCounter = 0;
service.registerProvider('test', new NullFileSystemProvider(() => {
return toDisposable(() => {
disposeCounter++;
});
}));
await service.activateProvider('test');
const resource1 = URI.parse('test://foo/bar1');
const watcher1Disposable = service.watch(resource1);
await timeout(0); // service.watch() is async
assert.equal(disposeCounter, 0);
watcher1Disposable.dispose();
assert.equal(disposeCounter, 1);
disposeCounter = 0;
const resource2 = URI.parse('test://foo/bar2');
const watcher2Disposable1 = service.watch(resource2);
const watcher2Disposable2 = service.watch(resource2);
const watcher2Disposable3 = service.watch(resource2);
await timeout(0); // service.watch() is async
assert.equal(disposeCounter, 0);
watcher2Disposable1.dispose();
assert.equal(disposeCounter, 0);
watcher2Disposable2.dispose();
assert.equal(disposeCounter, 0);
watcher2Disposable3.dispose();
assert.equal(disposeCounter, 1);
disposeCounter = 0;
const resource3 = URI.parse('test://foo/bar3');
const watcher3Disposable1 = service.watch(resource3);
const watcher3Disposable2 = service.watch(resource3, { recursive: true, excludes: [] });
await timeout(0); // service.watch() is async
assert.equal(disposeCounter, 0);
watcher3Disposable1.dispose();
assert.equal(disposeCounter, 1);
watcher3Disposable2.dispose();
assert.equal(disposeCounter, 2);
});
});
\ No newline at end of file
......@@ -17,7 +17,7 @@ import { ColorThemeData } from './colorThemeData';
import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService';
import { Event, Emitter } from 'vs/base/common/event';
import { registerFileIconThemeSchemas } from 'vs/workbench/services/themes/common/fileIconThemeSchema';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { ColorThemeStore } from 'vs/workbench/services/themes/browser/colorThemeStore';
import { FileIconThemeStore } from 'vs/workbench/services/themes/common/fileIconThemeStore';
import { FileIconThemeData } from 'vs/workbench/services/themes/common/fileIconThemeData';
......@@ -75,11 +75,13 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
private container: HTMLElement;
private readonly onColorThemeChange: Emitter<IColorTheme>;
private watchedColorThemeLocation: URI | undefined;
private watchedColorThemeDisposable: IDisposable;
private iconThemeStore: FileIconThemeStore;
private currentIconTheme: FileIconThemeData;
private readonly onFileIconThemeChange: Emitter<IFileIconTheme>;
private watchedIconThemeLocation: URI | undefined;
private watchedIconThemeDisposable: IDisposable;
private themingParticipantChangeListener: IDisposable;
......@@ -393,13 +395,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
}
if (this.fileService && !resources.isEqual(newTheme.location, this.watchedColorThemeLocation)) {
if (this.watchedColorThemeLocation) {
this.fileService.unwatch(this.watchedColorThemeLocation);
this.watchedColorThemeLocation = undefined;
}
this.watchedColorThemeDisposable = dispose(this.watchedColorThemeDisposable);
this.watchedColorThemeLocation = undefined;
if (newTheme.location && (newTheme.watch || !!this.environmentService.extensionDevelopmentLocationURI)) {
this.watchedColorThemeLocation = newTheme.location;
this.fileService.watch(this.watchedColorThemeLocation);
this.watchedColorThemeDisposable = this.fileService.watch(newTheme.location);
}
}
......@@ -511,13 +512,12 @@ export class WorkbenchThemeService implements IWorkbenchThemeService {
}
if (this.fileService && !resources.isEqual(iconThemeData.location, this.watchedIconThemeLocation)) {
if (this.watchedIconThemeLocation) {
this.fileService.unwatch(this.watchedIconThemeLocation);
this.watchedIconThemeLocation = undefined;
}
this.watchedIconThemeDisposable = dispose(this.watchedIconThemeDisposable);
this.watchedIconThemeLocation = undefined;
if (iconThemeData.location && (iconThemeData.watch || !!this.environmentService.extensionDevelopmentLocationURI)) {
this.watchedIconThemeLocation = iconThemeData.location;
this.fileService.watch(this.watchedIconThemeLocation);
this.watchedIconThemeDisposable = this.fileService.watch(iconThemeData.location);
}
}
......
......@@ -1032,10 +1032,8 @@ export class TestFileService implements IFileService {
return Promise.resolve();
}
watch(_resource: URI): void {
}
unwatch(_resource: URI): void {
watch(_resource: URI): IDisposable {
return Disposable.None;
}
getWriteEncoding(_resource: URI): IResourceEncoding {
......@@ -1582,7 +1580,9 @@ export class NullFileSystemProvider implements IFileSystemProvider {
onDidChangeCapabilities: Event<void> = Event.None;
onDidChangeFile: Event<IFileChange[]> = Event.None;
watch(resource: URI, opts: IWatchOptions): IDisposable { return Disposable.None; }
constructor(private disposableFactory: () => IDisposable = () => Disposable.None) { }
watch(resource: URI, opts: IWatchOptions): IDisposable { return this.disposableFactory(); }
stat(resource: URI): Promise<IStat> { return Promise.resolve(undefined!); }
mkdir(resource: URI): Promise<void> { return Promise.resolve(undefined!); }
readdir(resource: URI): Promise<[string, FileType][]> { return Promise.resolve(undefined!); }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册