Skip to content

Commit c98431e

Browse files
committed
microsoft#59478 Use file service to read user configuration
1 parent 23ffed0 commit c98431e

4 files changed

Lines changed: 128 additions & 70 deletions

File tree

src/vs/platform/configuration/node/configuration.ts

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,17 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6-
import { Disposable } from 'vs/base/common/lifecycle';
6+
import { Disposable, toDisposable } from 'vs/base/common/lifecycle';
77
import { onUnexpectedError } from 'vs/base/common/errors';
88
import { ConfigurationModelParser, ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
99
import { ConfigWatcher } from 'vs/base/node/config';
1010
import { Event, Emitter } from 'vs/base/common/event';
11+
import { RunOnceScheduler } from 'vs/base/common/async';
12+
import { URI } from 'vs/base/common/uri';
13+
import { IFileService, FileChangesEvent } from 'vs/platform/files/common/files';
14+
import * as resources from 'vs/base/common/resources';
1115

12-
export class UserConfiguration extends Disposable {
16+
export class NodeBasedUserConfiguration extends Disposable {
1317

1418
private userConfigModelWatcher: ConfigWatcher<ConfigurationModelParser>;
1519
private initializePromise: Promise<void>;
@@ -50,4 +54,53 @@ export class UserConfiguration extends Disposable {
5054
return this.initialize().then(() => new Promise<ConfigurationModel>(c => this.userConfigModelWatcher.reload(userConfigModelParser => c(userConfigModelParser.configurationModel))));
5155
}
5256

57+
}
58+
59+
export class FileServiceBasedUserConfiguration extends Disposable {
60+
61+
private readonly reloadConfigurationScheduler: RunOnceScheduler;
62+
protected readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
63+
readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
64+
65+
constructor(
66+
private readonly configurationResource: URI,
67+
private readonly fileService: IFileService
68+
) {
69+
super();
70+
71+
this._register(fileService.onFileChanges(e => this.handleFileEvents(e)));
72+
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reload().then(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)), 50));
73+
this.fileService.watchFileChanges(this.configurationResource);
74+
this._register(toDisposable(() => this.fileService.unwatchFileChanges(this.configurationResource)));
75+
}
76+
77+
initialize(): Promise<ConfigurationModel> {
78+
return this.reload();
79+
}
80+
81+
reload(): Promise<ConfigurationModel> {
82+
return this.fileService.resolveContent(this.configurationResource)
83+
.then(content => content.value, () => {
84+
// File not found
85+
return '';
86+
}).then(content => {
87+
const parser = new ConfigurationModelParser(this.configurationResource.toString());
88+
parser.parse(content);
89+
return parser.configurationModel;
90+
});
91+
}
92+
93+
private handleFileEvents(event: FileChangesEvent): void {
94+
const events = event.changes;
95+
96+
let affectedByChanges = false;
97+
// Find changes that affect workspace file
98+
for (let i = 0, len = events.length; i < len && !affectedByChanges; i++) {
99+
affectedByChanges = resources.isEqual(this.configurationResource, events[i].resource);
100+
}
101+
102+
if (affectedByChanges) {
103+
this.reloadConfigurationScheduler.schedule();
104+
}
105+
}
53106
}

src/vs/platform/configuration/node/configurationService.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ import { DefaultConfigurationModel, Configuration, ConfigurationChangeEvent, Con
1111
import { Event, Emitter } from 'vs/base/common/event';
1212
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
1313
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
14-
import { UserConfiguration } from 'vs/platform/configuration/node/configuration';
14+
import { NodeBasedUserConfiguration } from 'vs/platform/configuration/node/configuration';
1515

1616
export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable {
1717

1818
_serviceBrand: any;
1919

2020
private _configuration: Configuration;
21-
private userConfiguration: UserConfiguration;
21+
private userConfiguration: NodeBasedUserConfiguration;
2222

2323
private readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
2424
readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
@@ -28,7 +28,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
2828
) {
2929
super();
3030

31-
this.userConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath));
31+
this.userConfiguration = this._register(new NodeBasedUserConfiguration(environmentService.appSettingsPath));
3232

3333
// Initialize
3434
const defaults = new DefaultConfigurationModel();

src/vs/workbench/services/configuration/node/configuration.ts

Lines changed: 56 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import * as collections from 'vs/base/common/collections';
1313
import { Disposable, IDisposable, dispose, toDisposable } from 'vs/base/common/lifecycle';
1414
import { RunOnceScheduler, Delayer } from 'vs/base/common/async';
1515
import { FileChangeType, FileChangesEvent, IContent, IFileService } from 'vs/platform/files/common/files';
16-
import { ConfigurationModel, ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels';
16+
import { ConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
1717
import { WorkspaceConfigurationModelParser, FolderSettingsModelParser, StandaloneConfigurationModelParser } from 'vs/workbench/services/configuration/common/configurationModels';
1818
import { FOLDER_SETTINGS_PATH, TASKS_CONFIGURATION_KEY, FOLDER_SETTINGS_NAME, LAUNCH_CONFIGURATION_KEY } from 'vs/workbench/services/configuration/common/configuration';
1919
import { IStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
@@ -26,14 +26,52 @@ import { equals } from 'vs/base/common/objects';
2626
import { Schemas } from 'vs/base/common/network';
2727
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
2828
import { IConfigurationModel, compare } from 'vs/platform/configuration/common/configuration';
29+
import { FileServiceBasedUserConfiguration, NodeBasedUserConfiguration } from 'vs/platform/configuration/node/configuration';
30+
31+
export class LocalUserConfiguration extends Disposable {
32+
33+
private readonly userConfigurationResource: URI;
34+
private userConfiguration: NodeBasedUserConfiguration | FileServiceBasedUserConfiguration;
35+
private changeDisposable: IDisposable = Disposable.None;
36+
37+
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
38+
public readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
39+
40+
constructor(
41+
environmentService: IEnvironmentService
42+
) {
43+
super();
44+
this.userConfigurationResource = URI.file(environmentService.appSettingsPath);
45+
this.userConfiguration = this._register(new NodeBasedUserConfiguration(environmentService.appSettingsPath));
46+
this.changeDisposable = this._register(this.userConfiguration.onDidChangeConfiguration(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)));
47+
}
48+
49+
initialize(): Promise<ConfigurationModel> {
50+
return this.userConfiguration.initialize();
51+
}
52+
53+
reload(): Promise<ConfigurationModel> {
54+
return this.userConfiguration.reload();
55+
}
56+
57+
async adopt(fileService: IFileService): Promise<ConfigurationModel | null> {
58+
if (this.userConfiguration instanceof NodeBasedUserConfiguration) {
59+
this.userConfiguration.dispose();
60+
dispose(this.changeDisposable);
61+
this.userConfiguration = this._register(new FileServiceBasedUserConfiguration(this.userConfigurationResource, fileService));
62+
this.changeDisposable = this._register(this.userConfiguration.onDidChangeConfiguration(configurationModel => this._onDidChangeConfiguration.fire(configurationModel)));
63+
}
64+
return null;
65+
}
66+
}
2967

3068
export class RemoteUserConfiguration extends Disposable {
3169

3270
private readonly _cachedConfiguration: CachedUserConfiguration;
3371
private _userConfiguration: FileServiceBasedUserConfiguration | CachedUserConfiguration;
3472

35-
private readonly _onDidUpdateConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
36-
public readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidUpdateConfiguration.event;
73+
private readonly _onDidChangeConfiguration: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
74+
public readonly onDidChangeConfiguration: Event<ConfigurationModel> = this._onDidChangeConfiguration.event;
3775

3876
constructor(
3977
remoteAuthority: string,
@@ -43,18 +81,22 @@ export class RemoteUserConfiguration extends Disposable {
4381
this._userConfiguration = this._cachedConfiguration = new CachedUserConfiguration(remoteAuthority, environmentService);
4482
}
4583

46-
load(): Promise<ConfigurationModel> {
47-
return this._userConfiguration.loadConfiguration();
84+
initialize(): Promise<ConfigurationModel> {
85+
return this._userConfiguration.initialize();
86+
}
87+
88+
reload(): Promise<ConfigurationModel> {
89+
return this._userConfiguration.reload();
4890
}
4991

5092
async adopt(configurationResource: URI | null, fileService: IFileService): Promise<ConfigurationModel | null> {
5193
if (this._userConfiguration instanceof CachedUserConfiguration) {
5294
const oldConfigurationModel = this._userConfiguration.getConfigurationModel();
5395
let newConfigurationModel = new ConfigurationModel();
5496
if (configurationResource) {
55-
this._userConfiguration = new FileServiceBasedUserConfiguration(configurationResource, oldConfigurationModel, fileService);
56-
this._register(this._userConfiguration.onDidChange(configurationModel => this.onDidUserConfigurationChange(configurationModel)));
57-
newConfigurationModel = await this._userConfiguration.loadConfiguration();
97+
this._userConfiguration = new FileServiceBasedUserConfiguration(configurationResource, fileService);
98+
this._register(this._userConfiguration.onDidChangeConfiguration(configurationModel => this.onDidUserConfigurationChange(configurationModel)));
99+
newConfigurationModel = await this._userConfiguration.initialize();
58100
}
59101
const { added, updated, removed } = compare(oldConfigurationModel, newConfigurationModel);
60102
if (added.length > 0 || updated.length > 0 || removed.length > 0) {
@@ -67,61 +109,14 @@ export class RemoteUserConfiguration extends Disposable {
67109

68110
private onDidUserConfigurationChange(configurationModel: ConfigurationModel): void {
69111
this.updateCache(configurationModel);
70-
this._onDidUpdateConfiguration.fire(configurationModel);
112+
this._onDidChangeConfiguration.fire(configurationModel);
71113
}
72114

73115
private updateCache(configurationModel: ConfigurationModel): Promise<void> {
74116
return this._cachedConfiguration.updateConfiguration(configurationModel);
75117
}
76118
}
77119

78-
class FileServiceBasedUserConfiguration extends Disposable {
79-
80-
private readonly reloadConfigurationScheduler: RunOnceScheduler;
81-
protected readonly _onDidChange: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
82-
readonly onDidChange: Event<ConfigurationModel> = this._onDidChange.event;
83-
84-
constructor(
85-
private readonly configurationResource: URI,
86-
private configurationModel: ConfigurationModel,
87-
private readonly fileService: IFileService
88-
) {
89-
super();
90-
91-
this._register(fileService.onFileChanges(e => this.handleFileEvents(e)));
92-
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.loadConfiguration().then(configurationModel => this._onDidChange.fire(configurationModel)), 50));
93-
this.fileService.watchFileChanges(this.configurationResource);
94-
this._register(toDisposable(() => this.fileService.unwatchFileChanges(this.configurationResource)));
95-
}
96-
97-
loadConfiguration(): Promise<ConfigurationModel> {
98-
return this.fileService.resolveContent(this.configurationResource)
99-
.then(content => content.value, e => {
100-
errors.onUnexpectedError(e);
101-
return '';
102-
}).then(content => {
103-
const parser = new ConfigurationModelParser(this.configurationResource.toString());
104-
parser.parse(content);
105-
this.configurationModel = parser.configurationModel;
106-
return this.configurationModel;
107-
});
108-
}
109-
110-
private handleFileEvents(event: FileChangesEvent): void {
111-
const events = event.changes;
112-
113-
let affectedByChanges = false;
114-
// Find changes that affect workspace file
115-
for (let i = 0, len = events.length; i < len && !affectedByChanges; i++) {
116-
affectedByChanges = resources.isEqual(this.configurationResource, events[i].resource);
117-
}
118-
119-
if (affectedByChanges) {
120-
this.reloadConfigurationScheduler.schedule();
121-
}
122-
}
123-
}
124-
125120
class CachedUserConfiguration extends Disposable {
126121

127122
private readonly _onDidChange: Emitter<ConfigurationModel> = this._register(new Emitter<ConfigurationModel>());
@@ -145,7 +140,11 @@ class CachedUserConfiguration extends Disposable {
145140
return this.configurationModel;
146141
}
147142

148-
loadConfiguration(): Promise<ConfigurationModel> {
143+
initialize(): Promise<ConfigurationModel> {
144+
return this.reload();
145+
}
146+
147+
reload(): Promise<ConfigurationModel> {
149148
return pfs.readFile(this.cachedConfigurationPath)
150149
.then(content => content.toString(), () => '')
151150
.then(content => {

src/vs/workbench/services/configuration/node/configurationService.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,8 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
2828
import product from 'vs/platform/product/node/product';
2929
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
3030
import { ConfigurationEditingService } from 'vs/workbench/services/configuration/common/configurationEditingService';
31-
import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration } from 'vs/workbench/services/configuration/node/configuration';
31+
import { WorkspaceConfiguration, FolderConfiguration, RemoteUserConfiguration, LocalUserConfiguration } from 'vs/workbench/services/configuration/node/configuration';
3232
import { JSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditingService';
33-
import { UserConfiguration } from 'vs/platform/configuration/node/configuration';
3433
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
3534
import { localize } from 'vs/nls';
3635
import { isEqual, dirname } from 'vs/base/common/resources';
@@ -47,7 +46,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
4746
private completeWorkspaceBarrier: Barrier;
4847
private _configuration: Configuration;
4948
private defaultConfiguration: DefaultConfigurationModel;
50-
private localUserConfiguration: UserConfiguration;
49+
private localUserConfiguration: LocalUserConfiguration;
5150
private remoteUserConfiguration: RemoteUserConfiguration | null = null;
5251
private workspaceConfiguration: WorkspaceConfiguration;
5352
private cachedFolderConfigs: ResourceMap<FolderConfiguration>;
@@ -75,13 +74,13 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
7574

7675
this.completeWorkspaceBarrier = new Barrier();
7776
this.defaultConfiguration = new DefaultConfigurationModel();
78-
this.localUserConfiguration = this._register(new UserConfiguration(environmentService.appSettingsPath));
77+
this.localUserConfiguration = this._register(new LocalUserConfiguration(environmentService));
78+
this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));
7979
if (configuration.remoteAuthority) {
8080
this.remoteUserConfiguration = this._register(new RemoteUserConfiguration(configuration.remoteAuthority, environmentService));
8181
this._register(this.remoteUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onRemoteUserConfigurationChanged(userConfiguration)));
8282
}
8383
this.workspaceConfiguration = this._register(new WorkspaceConfiguration(environmentService));
84-
this._register(this.localUserConfiguration.onDidChangeConfiguration(userConfiguration => this.onLocalUserConfigurationChanged(userConfiguration)));
8584
this._register(this.workspaceConfiguration.onDidUpdateConfiguration(() => this.onWorkspaceConfigurationChanged()));
8685

8786
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidSchemaChange(e => this.registerConfigurationSchemas()));
@@ -296,6 +295,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
296295
acquireFileService(fileService: IFileService): void {
297296
this.fileService = fileService;
298297
const changedWorkspaceFolders: IWorkspaceFolder[] = [];
298+
this.localUserConfiguration.adopt(fileService);
299299
Promise.all([this.workspaceConfiguration.adopt(fileService), ...this.cachedFolderConfigs.values()
300300
.map(folderConfiguration => folderConfiguration.adopt(fileService)
301301
.then(result => {
@@ -446,12 +446,12 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
446446
}
447447

448448
private initializeUserConfiguration(): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> {
449-
return Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.load() : Promise.resolve(new ConfigurationModel())])
449+
return Promise.all([this.localUserConfiguration.initialize(), this.remoteUserConfiguration ? this.remoteUserConfiguration.initialize() : Promise.resolve(new ConfigurationModel())])
450450
.then(([local, remote]) => ({ local, remote }));
451451
}
452452

453453
private reloadUserConfiguration(key?: string): Promise<{ local: ConfigurationModel, remote: ConfigurationModel }> {
454-
return Promise.all([this.localUserConfiguration.reload(), this.remoteUserConfiguration ? this.remoteUserConfiguration.load() : Promise.resolve(new ConfigurationModel())])
454+
return Promise.all([this.localUserConfiguration.reload(), this.remoteUserConfiguration ? this.remoteUserConfiguration.reload() : Promise.resolve(new ConfigurationModel())])
455455
.then(([local, remote]) => ({ local, remote }));
456456
}
457457

@@ -645,7 +645,13 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
645645
.then(() => {
646646
switch (target) {
647647
case ConfigurationTarget.USER:
648-
return this.reloadUserConfiguration().then(_ => Promise.resolve());
648+
return this.reloadUserConfiguration()
649+
.then(({ local, remote }) => {
650+
this.onLocalUserConfigurationChanged(local);
651+
if (this.remoteUserConfiguration) {
652+
this.onRemoteUserConfigurationChanged(remote);
653+
}
654+
});
649655
case ConfigurationTarget.WORKSPACE:
650656
return this.reloadWorkspaceConfiguration();
651657
case ConfigurationTarget.WORKSPACE_FOLDER:

0 commit comments

Comments
 (0)