configurationService.ts 6.1 KB
Newer Older
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

J
Johannes Rieken 已提交
7
import { ConfigWatcher } from 'vs/base/node/config';
8
import { Registry } from 'vs/platform/registry/common/platform';
9
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
B
Benjamin Pasero 已提交
10
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
11
import { IConfigurationService, IConfigurationChangeEvent, IConfigurationOverrides, ConfigurationTarget, toConfigurationUpdateEvent, ConfigurationModel, Configuration, compare } from 'vs/platform/configuration/common/configuration';
12
import { CustomConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/model';
J
Johannes Rieken 已提交
13 14
import Event, { Emitter } from 'vs/base/common/event';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
B
Benjamin Pasero 已提交
15
import { onUnexpectedError } from 'vs/base/common/errors';
16 17 18 19 20 21 22 23 24 25 26
import URI from 'vs/base/common/uri';
import { TPromise } from 'vs/base/common/winjs.base';
import { equals } from 'vs/base/common/objects';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';

export function isConfigurationOverrides(thing: any): thing is IConfigurationOverrides {
	return thing
		&& typeof thing === 'object'
		&& (!thing.overrideIdentifier || typeof thing.overrideIdentifier === 'string')
		&& (!thing.resource || thing.resource instanceof URI);
}
27

28
export class ConfigurationService<T> extends Disposable implements IConfigurationService, IDisposable {
29 30 31

	_serviceBrand: any;

32
	private _configuration: Configuration<T>;
33
	private userConfigModelWatcher: ConfigWatcher<ConfigurationModel<T>>;
34

35 36
	private _onDidUpdateConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
	readonly onDidUpdateConfiguration: Event<IConfigurationChangeEvent> = this._onDidUpdateConfiguration.event;
37 38 39 40

	constructor(
		@IEnvironmentService environmentService: IEnvironmentService
	) {
41 42 43
		super();

		this.userConfigModelWatcher = new ConfigWatcher(environmentService.appSettingsPath, {
44
			changeBufferDelay: 300, onError: error => onUnexpectedError(error), defaultConfig: new CustomConfigurationModel<T>(null, environmentService.appSettingsPath), parse: (content: string, parseErrors: any[]) => {
45
				const userConfigModel = new CustomConfigurationModel<T>(content, environmentService.appSettingsPath);
46 47 48 49
				parseErrors = [...userConfigModel.errors];
				return userConfigModel;
			}
		});
B
Benjamin Pasero 已提交
50
		this._register(this.userConfigModelWatcher);
51

52 53
		this.reset();

B
Benjamin Pasero 已提交
54
		// Listeners
55
		this._register(this.userConfigModelWatcher.onDidUpdateConfiguration(() => this.onDidUpdateConfigModel()));
56
		this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidRegisterConfiguration(configurationProperties => this.onDidRegisterConfiguration(configurationProperties)));
57 58
	}

59 60
	get configuration(): Configuration<any> {
		return this._configuration;
61 62
	}

63 64 65 66 67 68 69 70
	getConfiguration<T>(): T
	getConfiguration<T>(section: string): T
	getConfiguration<T>(overrides: IConfigurationOverrides): T
	getConfiguration<T>(section: string, overrides: IConfigurationOverrides): T
	getConfiguration(arg1?: any, arg2?: any): any {
		const section = typeof arg1 === 'string' ? arg1 : void 0;
		const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : void 0;
		return this.configuration.getValue(section, overrides);
71 72
	}

73 74
	getValue(key: string, overrides: IConfigurationOverrides): any {
		return this.configuration.getValue2(key, overrides);
B
Benjamin Pasero 已提交
75 76
	}

77 78 79 80 81 82
	updateValue(key: string, value: any): TPromise<void>
	updateValue(key: string, value: any, overrides: IConfigurationOverrides): TPromise<void>
	updateValue(key: string, value: any, target: ConfigurationTarget): TPromise<void>
	updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): TPromise<void>
	updateValue(key: string, value: any, arg3?: any, arg4?: any): TPromise<void> {
		return TPromise.wrapError(new Error('not supported'));
83
	}
84

85
	inspect<T>(key: string): {
86 87 88 89 90 91 92
		default: T,
		user: T,
		workspace: T,
		workspaceFolder: T
		value: T
	} {
		return this.configuration.lookup<T>(key);
93 94
	}

95
	keys(): {
96 97 98 99 100 101
		default: string[];
		user: string[];
		workspace: string[];
		workspaceFolder: string[];
	} {
		return this.configuration.keys();
B
Benjamin Pasero 已提交
102 103
	}

104 105 106
	reloadConfiguration(folder?: IWorkspaceFolder): TPromise<void> {
		return folder ? TPromise.as(null) :
			new TPromise((c, e) => this.userConfigModelWatcher.reload(() => c(this.onDidUpdateConfigModel())));
107
	}
108

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
	private onDidUpdateConfigModel(): void {
		let changedKeys = [];
		const { added, updated, removed } = compare(this._configuration.user, this.userConfigModelWatcher.getConfig());
		changedKeys = [...added, ...updated, ...removed];
		if (changedKeys.length) {
			const oldConfiguartion = this._configuration;
			this.reset();
			changedKeys = changedKeys.filter(key => !equals(oldConfiguartion.getValue2(key), this._configuration.getValue2(key)));
			if (changedKeys.length) {
				this.trigger(changedKeys, ConfigurationTarget.USER);
			}
		}
	}

	private onDidRegisterConfiguration(keys: string[]): void {
		this.reset(); // reset our caches
		this.trigger(keys, ConfigurationTarget.DEFAULT);
	}

	private reset(): void {
129
		const defaults = new DefaultConfigurationModel<T>();
130
		const user = this.userConfigModelWatcher.getConfig();
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
		this._configuration = new Configuration(defaults, user);
	}

	private trigger(keys: string[], source: ConfigurationTarget): void {
		this._onDidUpdateConfiguration.fire(toConfigurationUpdateEvent(keys, source, this.getTargetConfiguration(source)));
	}

	private getTargetConfiguration(target: ConfigurationTarget): any {
		switch (target) {
			case ConfigurationTarget.DEFAULT:
				return this._configuration.defaults.contents;
			case ConfigurationTarget.USER:
				return this._configuration.user.contents;
		}
		return {};
146 147
	}
}