From c98431ec76dcb438a794abf22c452feafa8619c2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 25 Mar 2019 14:23:09 +0100 Subject: [PATCH] #59478 Use file service to read user configuration --- .../configuration/node/configuration.ts | 57 ++++++++- .../node/configurationService.ts | 6 +- .../configuration/node/configuration.ts | 113 +++++++++--------- .../node/configurationService.ts | 22 ++-- 4 files changed, 128 insertions(+), 70 deletions(-) diff --git a/src/vs/platform/configuration/node/configuration.ts b/src/vs/platform/configuration/node/configuration.ts index da87cfd3dec..3dad4cda386 100644 --- a/src/vs/platform/configuration/node/configuration.ts +++ b/src/vs/platform/configuration/node/configuration.ts @@ -3,13 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } 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'; import { Event, Emitter } from 'vs/base/common/event'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { URI } from 'vs/base/common/uri'; +import { IFileService, FileChangesEvent } from 'vs/platform/files/common/files'; +import * as resources from 'vs/base/common/resources'; -export class UserConfiguration extends Disposable { +export class NodeBasedUserConfiguration extends Disposable { private userConfigModelWatcher: ConfigWatcher; private initializePromise: Promise; @@ -50,4 +54,53 @@ export class UserConfiguration extends Disposable { return this.initialize().then(() => new Promise(c => this.userConfigModelWatcher.reload(userConfigModelParser => c(userConfigModelParser.configurationModel)))); } +} + +export class FileServiceBasedUserConfiguration extends Disposable { + + private readonly reloadConfigurationScheduler: RunOnceScheduler; + protected readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); + readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; + + constructor( + private readonly configurationResource: URI, + private readonly fileService: IFileService + ) { + super(); + + 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.watchFileChanges(this.configurationResource); + this._register(toDisposable(() => this.fileService.unwatchFileChanges(this.configurationResource))); + } + + initialize(): Promise { + return this.reload(); + } + + reload(): Promise { + return this.fileService.resolveContent(this.configurationResource) + .then(content => content.value, () => { + // File not found + return ''; + }).then(content => { + const parser = new ConfigurationModelParser(this.configurationResource.toString()); + parser.parse(content); + return parser.configurationModel; + }); + } + + private handleFileEvents(event: FileChangesEvent): void { + const events = event.changes; + + let affectedByChanges = false; + // Find changes that affect workspace file + for (let i = 0, len = events.length; i < len && !affectedByChanges; i++) { + affectedByChanges = resources.isEqual(this.configurationResource, events[i].resource); + } + + if (affectedByChanges) { + this.reloadConfigurationScheduler.schedule(); + } + } } \ No newline at end of file diff --git a/src/vs/platform/configuration/node/configurationService.ts b/src/vs/platform/configuration/node/configurationService.ts index 21d827ec0f5..f990bc049fb 100644 --- a/src/vs/platform/configuration/node/configurationService.ts +++ b/src/vs/platform/configuration/node/configurationService.ts @@ -11,14 +11,14 @@ import { DefaultConfigurationModel, Configuration, ConfigurationChangeEvent, Con import { Event, Emitter } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { UserConfiguration } from 'vs/platform/configuration/node/configuration'; +import { NodeBasedUserConfiguration } from 'vs/platform/configuration/node/configuration'; export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable { _serviceBrand: any; private _configuration: Configuration; - private userConfiguration: UserConfiguration; + private userConfiguration: NodeBasedUserConfiguration; private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; @@ -28,7 +28,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe ) { super(); - this.userConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath)); + this.userConfiguration = this._register(new NodeBasedUserConfiguration(environmentService.appSettingsPath)); // Initialize const defaults = new DefaultConfigurationModel(); diff --git a/src/vs/workbench/services/configuration/node/configuration.ts b/src/vs/workbench/services/configuration/node/configuration.ts index 13cf02bc26b..31ce03dba1a 100644 --- a/src/vs/workbench/services/configuration/node/configuration.ts +++ b/src/vs/workbench/services/configuration/node/configuration.ts @@ -13,7 +13,7 @@ import * as collections from 'vs/base/common/collections'; import { Disposable, IDisposable, dispose, toDisposable } 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, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels'; +import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels'; import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration'; import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; @@ -26,14 +26,52 @@ import { equals } from 'vs/base/common/objects'; import { Schemas } from 'vs/base/common/network'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationModel, compare } from 'vs/platform/configuration/common/configuration'; +import { FileServiceBasedUserConfiguration, NodeBasedUserConfiguration } from 'vs/platform/configuration/node/configuration'; + +export class LocalUserConfiguration extends Disposable { + + private readonly userConfigurationResource: URI; + private userConfiguration: NodeBasedUserConfiguration | FileServiceBasedUserConfiguration; + private changeDisposable: IDisposable = Disposable.None; + + private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; + + constructor( + environmentService: IEnvironmentService + ) { + super(); + this.userConfigurationResource = URI.file(environmentService.appSettingsPath); + this.userConfiguration = this._register(new NodeBasedUserConfiguration(environmentService.appSettingsPath)); + this.changeDisposable = this._register(this.userConfiguration.onDidChangeConfiguration(configurationModel => this._onDidChangeConfiguration.fire(configurationModel))); + } + + initialize(): Promise { + return this.userConfiguration.initialize(); + } + + reload(): Promise { + return this.userConfiguration.reload(); + } + + async adopt(fileService: IFileService): Promise { + if (this.userConfiguration instanceof NodeBasedUserConfiguration) { + this.userConfiguration.dispose(); + dispose(this.changeDisposable); + this.userConfiguration = this._register(new FileServiceBasedUserConfiguration(this.userConfigurationResource, fileService)); + this.changeDisposable = this._register(this.userConfiguration.onDidChangeConfiguration(configurationModel => this._onDidChangeConfiguration.fire(configurationModel))); + } + return null; + } +} export class RemoteUserConfiguration extends Disposable { private readonly _cachedConfiguration: CachedUserConfiguration; private _userConfiguration: FileServiceBasedUserConfiguration | CachedUserConfiguration; - private readonly _onDidUpdateConfiguration: Emitter = this._register(new Emitter()); - public readonly onDidChangeConfiguration: Event = this._onDidUpdateConfiguration.event; + private readonly _onDidChangeConfiguration: Emitter = this._register(new Emitter()); + public readonly onDidChangeConfiguration: Event = this._onDidChangeConfiguration.event; constructor( remoteAuthority: string, @@ -43,8 +81,12 @@ export class RemoteUserConfiguration extends Disposable { this._userConfiguration = this._cachedConfiguration = new CachedUserConfiguration(remoteAuthority, environmentService); } - load(): Promise { - return this._userConfiguration.loadConfiguration(); + initialize(): Promise { + return this._userConfiguration.initialize(); + } + + reload(): Promise { + return this._userConfiguration.reload(); } async adopt(configurationResource: URI | null, fileService: IFileService): Promise { @@ -52,9 +94,9 @@ export class RemoteUserConfiguration extends Disposable { const oldConfigurationModel = this._userConfiguration.getConfigurationModel(); let newConfigurationModel = new ConfigurationModel(); if (configurationResource) { - this._userConfiguration = new FileServiceBasedUserConfiguration(configurationResource, oldConfigurationModel, fileService); - this._register(this._userConfiguration.onDidChange(configurationModel => this.onDidUserConfigurationChange(configurationModel))); - newConfigurationModel = await this._userConfiguration.loadConfiguration(); + this._userConfiguration = new FileServiceBasedUserConfiguration(configurationResource, fileService); + this._register(this._userConfiguration.onDidChangeConfiguration(configurationModel => this.onDidUserConfigurationChange(configurationModel))); + newConfigurationModel = await this._userConfiguration.initialize(); } const { added, updated, removed } = compare(oldConfigurationModel, newConfigurationModel); if (added.length > 0 || updated.length > 0 || removed.length > 0) { @@ -67,7 +109,7 @@ export class RemoteUserConfiguration extends Disposable { private onDidUserConfigurationChange(configurationModel: ConfigurationModel): void { this.updateCache(configurationModel); - this._onDidUpdateConfiguration.fire(configurationModel); + this._onDidChangeConfiguration.fire(configurationModel); } private updateCache(configurationModel: ConfigurationModel): Promise { @@ -75,53 +117,6 @@ export class RemoteUserConfiguration extends Disposable { } } -class FileServiceBasedUserConfiguration extends Disposable { - - private readonly reloadConfigurationScheduler: RunOnceScheduler; - protected readonly _onDidChange: Emitter = this._register(new Emitter()); - readonly onDidChange: Event = this._onDidChange.event; - - constructor( - private readonly configurationResource: URI, - private configurationModel: ConfigurationModel, - private readonly fileService: IFileService - ) { - super(); - - this._register(fileService.onFileChanges(e => this.handleFileEvents(e))); - this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.loadConfiguration().then(configurationModel => this._onDidChange.fire(configurationModel)), 50)); - this.fileService.watchFileChanges(this.configurationResource); - this._register(toDisposable(() => this.fileService.unwatchFileChanges(this.configurationResource))); - } - - loadConfiguration(): Promise { - return this.fileService.resolveContent(this.configurationResource) - .then(content => content.value, e => { - errors.onUnexpectedError(e); - return ''; - }).then(content => { - const parser = new ConfigurationModelParser(this.configurationResource.toString()); - parser.parse(content); - this.configurationModel = parser.configurationModel; - return this.configurationModel; - }); - } - - private handleFileEvents(event: FileChangesEvent): void { - const events = event.changes; - - let affectedByChanges = false; - // Find changes that affect workspace file - for (let i = 0, len = events.length; i < len && !affectedByChanges; i++) { - affectedByChanges = resources.isEqual(this.configurationResource, events[i].resource); - } - - if (affectedByChanges) { - this.reloadConfigurationScheduler.schedule(); - } - } -} - class CachedUserConfiguration extends Disposable { private readonly _onDidChange: Emitter = this._register(new Emitter()); @@ -145,7 +140,11 @@ class CachedUserConfiguration extends Disposable { return this.configurationModel; } - loadConfiguration(): Promise { + initialize(): Promise { + return this.reload(); + } + + reload(): Promise { return pfs.readFile(this.cachedConfigurationPath) .then(content => content.toString(), () => '') .then(content => { diff --git a/src/vs/workbench/services/configuration/node/configurationService.ts b/src/vs/workbench/services/configuration/node/configurationService.ts index ec6ee63a186..df6d6bd1018 100644 --- a/src/vs/workbench/services/configuration/node/configurationService.ts +++ b/src/vs/workbench/services/configuration/node/configurationService.ts @@ -28,9 +28,8 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import product from 'vs/platform/product/node/product'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditingService'; -import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration } from 'vs/workbench/services/configuration/node/configuration'; +import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, LocalUserConfiguration } from 'vs/workbench/services/configuration/node/configuration'; import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService'; -import { UserConfiguration } from 'vs/platform/configuration/node/configuration'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { localize } from 'vs/nls'; import { isEqual, dirname } from 'vs/base/common/resources'; @@ -47,7 +46,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private completeWorkspaceBarrier: Barrier; private _configuration: Configuration; private defaultConfiguration: DefaultConfigurationModel; - private localUserConfiguration: UserConfiguration; + private localUserConfiguration: LocalUserConfiguration; private remoteUserConfiguration: RemoteUserConfiguration | null = null; private workspaceConfiguration: WorkspaceConfiguration; private cachedFolderConfigs: ResourceMap; @@ -75,13 +74,13 @@ export class WorkspaceService extends Disposable implements IConfigurationServic this.completeWorkspaceBarrier = new Barrier(); this.defaultConfiguration = new DefaultConfigurationModel(); - this.localUserConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath)); + this.localUserConfiguration = this._register(new LocalUserConfiguration(environmentService)); + this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); if (configuration.remoteAuthority) { this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(configuration.remoteAuthority, environmentService)); this._register(this.remoteUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onRemoteUserConfigurationChanged(userConfiguration))); } this.workspaceConfiguration = this._register(new WorkspaceConfiguration(environmentService)); - this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration))); this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged())); this._register(Registry.as(Extensions.Configuration).onDidSchemaChange(e => this.registerConfigurationSchemas())); @@ -296,6 +295,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic acquireFileService(fileService: IFileService): void { this.fileService = fileService; const changedWorkspaceFolders: IWorkspaceFolder[] = []; + this.localUserConfiguration.adopt(fileService); Promise.all([this.workspaceConfiguration.adopt(fileService), ...this.cachedFolderConfigs.values() .map(folderConfiguration => folderConfiguration.adopt(fileService) .then(result => { @@ -446,12 +446,12 @@ export class WorkspaceService extends Disposable implements IConfigurationServic } private initializeUserConfiguration(): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> { - return Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.load() : Promise.resolve(new ConfigurationModel())]) + return Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel())]) .then(([local, remote]) => ({ local, remote })); } private reloadUserConfiguration(key?: string): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> { - return Promise.all([this.localUserConfiguration.reload(), this.remoteUserConfiguration ? this.remoteUserConfiguration.load() : Promise.resolve(new ConfigurationModel())]) + return Promise.all([this.localUserConfiguration.reload(), this.remoteUserConfiguration ? this.remoteUserConfiguration.reload() : Promise.resolve(new ConfigurationModel())]) .then(([local, remote]) => ({ local, remote })); } @@ -645,7 +645,13 @@ export class WorkspaceService extends Disposable implements IConfigurationServic .then(() => { switch (target) { case ConfigurationTarget.USER: - return this.reloadUserConfiguration().then(_ => Promise.resolve()); + return this.reloadUserConfiguration() + .then(({ local, remote }) => { + this.onLocalUserConfigurationChanged(local); + if (this.remoteUserConfiguration) { + this.onRemoteUserConfigurationChanged(remote); + } + }); case ConfigurationTarget.WORKSPACE: return this.reloadWorkspaceConfiguration(); case ConfigurationTarget.WORKSPACE_FOLDER: -- GitLab