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

263 LOC · 227 covered · 36 uncovered · 52 ranges · 2110 concepts · 14 introducers · 1058 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- configurationService.ts ×18
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { distinct, equals as arrayEquals } from '../../../base/common/arrays.js';
7 > import { Queue, RunOnceScheduler } from '../../../base/common/async.js';
8 > import { VSBuffer } from '../../../base/common/buffer.js';
9 > import { Emitter, Event } from '../../../base/common/event.js';
10 > import { JSONPath, ParseError, parse } from '../../../base/common/json.js';
11 > import { applyEdits, setProperty } from '../../../base/common/jsonEdit.js';
12 > import { Edit, FormattingOptions } from '../../../base/common/jsonFormatter.js';
13 > import { Disposable, IDisposable } from '../../../base/common/lifecycle.js';
14 > import { ResourceMap } from '../../../base/common/map.js';
15 > import { equals } from '../../../base/common/objects.js';
16 > import { OS, OperatingSystem } from '../../../base/common/platform.js';
17 > import { extUriBiasedIgnorePathCase } from '../../../base/common/resources.js';
18 > import { URI } from '../../../base/common/uri.js';
19 > import { ConfigurationTarget, IConfigurationChange, IConfigurationChangeEvent, IConfigurationData, IConfigurationOverrides, IConfigurationService, IConfigurationUpdateOptions, IConfigurationUpdateOverrides, IConfigurationValue, isConfigurationOverrides, isConfigurationUpdateOverrides } from './configuration.js';
20 > import { Configuration, ConfigurationChangeEvent, ConfigurationModel, UserSettings } from './configurationModels.js';
21 > import { keyFromOverrideIdentifiers } from './configurationRegistry.js';
22 > import { DefaultConfiguration, IPolicyConfiguration, NullPolicyConfiguration, PolicyConfiguration } from './configurations.js';
23 > import { FileOperationError, FileOperationResult, IFileService } from '../../files/common/files.js';
24 > import { ILogService } from '../../log/common/log.js';
25 > import { IPolicyService, NullPolicyService } from '../../policy/common/policy.js';
26 >
27 > export class ConfigurationService extends Disposable implements IConfigurationService, IDisposable {
28 >
29 > declare readonly _serviceBrand: undefined;
30 >
31 > private configuration: Configuration;
32 > private readonly defaultConfiguration: DefaultConfiguration;
33 > private readonly policyConfiguration: IPolicyConfiguration;
34 > private readonly userConfiguration: UserSettings;
35 > private readonly reloadConfigurationScheduler: RunOnceScheduler;
36 >
37 > private readonly _onDidChangeConfiguration: Emitter<IConfigurationChangeEvent> = this._register(new Emitter<IConfigurationChangeEvent>());
38 > readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent> = this._onDidChangeConfiguration.event;
39 >
40 > private readonly configurationEditing: ConfigurationEditing;
41 >
42 > constructor(
43 > private readonly settingsResource: URI, configurationService.ts ×2
44 > fileService: IFileService,
45 > policyService: IPolicyService,
46 > private readonly logService: ILogService,
47 > ) {
48 > super();
49 > this.defaultConfiguration = this._register(new DefaultConfiguration(logService));
50 > this.policyConfiguration = policyService instanceof NullPolicyService ? new NullPolicyConfiguration() : this._register(new PolicyConfiguration(this.defaultConfiguration, policyService, logService));
51 > this.userConfiguration = this._register(new UserSettings(this.settingsResource, {}, extUriBiasedIgnorePathCase, fileService, logService));
52 > this.configuration = new Configuration(
53 > this.defaultConfiguration.configurationModel,
54 > this.policyConfiguration.configurationModel,
55 > ConfigurationModel.createEmptyModel(logService),
56 > ConfigurationModel.createEmptyModel(logService),
57 > ConfigurationModel.createEmptyModel(logService),
58 > ConfigurationModel.createEmptyModel(logService),
59 > new ResourceMap<ConfigurationModel>(),
60 > ConfigurationModel.createEmptyModel(logService),
61 > new ResourceMap<ConfigurationModel>(),
62 > logService
63 > );
64 > this.configurationEditing = new ConfigurationEditing(settingsResource, fileService, this);
65 >
66 > this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reloadConfiguration(), 50));
67 > this._register(this.defaultConfiguration.onDidChangeConfiguration(({ defaults, properties }) => this.onDidDefaultConfigurationChange(defaults, properties)));
68 > this._register(this.policyConfiguration.onDidChangeConfiguration(model => this.onDidPolicyConfigurationChange(model)));
69 > this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule()));
70 > }
72 > async initialize(): Promise<void> {
73 > const [defaultModel, policyModel, userModel] = await Promise.all([this.defaultConfiguration.initialize(), this.policyConfiguration.initialize(), this.userConfiguration.loadConfiguration()]); configurationModels.ts ×2
74 > this.configuration = new Configuration(
75 > defaultModel,
76 > policyModel,
77 > ConfigurationModel.createEmptyModel(this.logService),
78 > userModel,
79 > ConfigurationModel.createEmptyModel(this.logService),
80 > ConfigurationModel.createEmptyModel(this.logService),
81 > new ResourceMap<ConfigurationModel>(),
82 > ConfigurationModel.createEmptyModel(this.logService),
83 > new ResourceMap<ConfigurationModel>(),
84 > this.logService
85 > );
86 > }
88 > getConfigurationData(): IConfigurationData {
89 return this.configuration.toData();
90 }
92 > getValue<T>(): T;
93 > getValue<T>(section: string): T;
94 > getValue<T>(overrides: IConfigurationOverrides): T;
95 > getValue<T>(section: string, overrides: IConfigurationOverrides): T;
96 > getValue(arg1?: unknown, arg2?: unknown): unknown {
97 > const section = typeof arg1 === 'string' ? arg1 : undefined; configurationService.ts ×1
98 > const overrides = isConfigurationOverrides(arg1) ? arg1 : isConfigurationOverrides(arg2) ? arg2 : {};
99 > return this.configuration.getValue(section, overrides, undefined);
100 > }
102 > updateValue(key: string, value: unknown): Promise<void>;
103 > updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides): Promise<void>;
104 > updateValue(key: string, value: unknown, target: ConfigurationTarget): Promise<void>;
105 > updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, target: ConfigurationTarget, options?: IConfigurationUpdateOptions): Promise<void>;
106 > async updateValue(key: string, value: unknown, arg3?: unknown, arg4?: unknown, options?: IConfigurationUpdateOptions): Promise<void> {
107 > const overrides: IConfigurationUpdateOverrides | undefined = isConfigurationUpdateOverrides(arg3) ? arg3 configurationService.ts ×6
108 > : isConfigurationOverrides(arg3) ? { resource: arg3.resource, overrideIdentifiers: arg3.overrideIdentifier ? [arg3.overrideIdentifier] : undefined } : undefined;
109 >
110 > const target: ConfigurationTarget | undefined = (overrides ? arg4 : arg3) as ConfigurationTarget | undefined;
111 > if (target !== undefined) {
112 > if (target !== ConfigurationTarget.USER_LOCAL && target !== ConfigurationTarget.USER) { configuration.ts ×1
113 > throw new Error(`Unable to write ${key} to target ${target}.`);
114 > }
115 > }
117 > if (overrides?.overrideIdentifiers) { configurationService.ts ×6
118 overrides.overrideIdentifiers = distinct(overrides.overrideIdentifiers);
119 overrides.overrideIdentifiers = overrides.overrideIdentifiers.length ? overrides.overrideIdentifiers : undefined;
120 }
122 > const inspect = this.inspect(key, { resource: overrides?.resource, overrideIdentifier: overrides?.overrideIdentifiers ? overrides.overrideIdentifiers[0] : undefined }); configurationService.ts ×6
123 > if (inspect.policyValue !== undefined) {
124 > throw new Error(`Unable to write ${key} because it is configured in system policy.`); configurationModels.ts ×1
125 > }
127 > // Remove the setting, if the value is same as default value
128 > if (equals(value, inspect.defaultValue)) {
129 > value = undefined; configurationService.ts ×1
130 > }
132 > if (overrides?.overrideIdentifiers?.length && overrides.overrideIdentifiers.length > 1) { configurationService.ts ×6
133 const overrideIdentifiers = overrides.overrideIdentifiers.sort();
134 const existingOverrides = this.configuration.localUserConfiguration.overrides.find(override => arrayEquals([...override.identifiers].sort(), overrideIdentifiers));
135 if (existingOverrides) {
136 overrides.overrideIdentifiers = existingOverrides.identifiers;
137 }
138 }
140 > const path = overrides?.overrideIdentifiers?.length ? [keyFromOverrideIdentifiers(overrides.overrideIdentifiers), key] : [key]; configurationService.ts ×6
141 >
142 > await this.configurationEditing.write(path, value);
143 > await this.reloadConfiguration(); configurationService.ts ×13
146 > inspect<T>(key: string, overrides: IConfigurationOverrides = {}): IConfigurationValue<T> {
147 > return this.configuration.inspect<T>(key, overrides, undefined); configurationService.ts ×1
148 > }
150 > keys(): {
151 default: string[];
152 policy: string[];
153 user: string[];
154 workspace: string[];
155 workspaceFolder: string[];
156 } {
157 return this.configuration.keys(undefined);
158 }
160 > async reloadConfiguration(): Promise<void> {
161 > const configurationModel = await this.userConfiguration.loadConfiguration(); configurationService.ts ×3
162 > this.onDidChangeUserConfiguration(configurationModel);
163 > }
165 > private onDidChangeUserConfiguration(userConfigurationModel: ConfigurationModel): void {
166 > const previous = this.configuration.toData(); configurationService.ts ×3
167 > const change = this.configuration.compareAndUpdateLocalUserConfiguration(userConfigurationModel);
168 > this.trigger(change, previous, ConfigurationTarget.USER);
169 > }
171 > private onDidDefaultConfigurationChange(defaultConfigurationModel: ConfigurationModel, properties: string[]): void {
172 > const previous = this.configuration.toData(); configurationService.ts ×1
173 > const change = this.configuration.compareAndUpdateDefaultConfiguration(defaultConfigurationModel, properties);
174 > this.trigger(change, previous, ConfigurationTarget.DEFAULT);
175 > }
177 > private onDidPolicyConfigurationChange(policyConfiguration: ConfigurationModel): void {
178 const previous = this.configuration.toData();
179 const change = this.configuration.compareAndUpdatePolicyConfiguration(policyConfiguration);
180 this.trigger(change, previous, ConfigurationTarget.DEFAULT);
181 }
183 > private trigger(configurationChange: IConfigurationChange, previous: IConfigurationData, source: ConfigurationTarget): void {
184 > const event = new ConfigurationChangeEvent(configurationChange, { data: previous }, this.configuration, undefined, this.logService); configurationService.ts ×3
185 > event.source = source;
186 > this._onDidChangeConfiguration.fire(event);
187 > }
189 >
190 > class ConfigurationEditing {
191 >
192 > private readonly queue: Queue<void>;
193 >
194 > constructor(
195 > private readonly settingsResource: URI, configurationService.ts ×2
196 > private readonly fileService: IFileService,
197 > private readonly configurationService: IConfigurationService,
198 > ) {
199 > this.queue = new Queue<void>();
200 > }
202 > write(path: JSONPath, value: unknown): Promise<void> {
203 > return this.queue.queue(() => this.doWriteConfiguration(path, value)); // queue up writes to prevent race conditions configurationService.ts ×13
204 > }
206 > private async doWriteConfiguration(path: JSONPath, value: unknown): Promise<void> {
207 > let content: string; configurationService.ts ×13
208 > try {
209 > const fileContent = await this.fileService.readFile(this.settingsResource);
210 > content = fileContent.value.toString(); configurationService.ts ×1
211 > } catch (error) { configurationService.ts ×13
212 > if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_NOT_FOUND) {
213 > content = '{}';
214 > } else {
215 throw error;
216 }
218 >
219 > const parseErrors: ParseError[] = [];
220 > parse(content, parseErrors, { allowTrailingComma: true, allowEmptyContent: true });
221 > if (parseErrors.length > 0) {
222 throw new Error('Unable to write into the settings file. Please open the file to correct errors/warnings in the file and try again.');
223 }
225 > const edits = this.getEdits(content, path, value);
226 > content = applyEdits(content, edits);
227 >
228 > await this.fileService.writeFile(this.settingsResource, VSBuffer.fromString(content));
229 > }
231 > private getEdits(content: string, path: JSONPath, value: unknown): Edit[] {
232 > const { tabSize, insertSpaces, eol } = this.formattingOptions; configurationService.ts ×13
233 >
234 > // With empty path the entire file is being replaced, so we just use JSON.stringify
235 > if (!path.length) {
236 const content = JSON.stringify(value, null, insertSpaces ? ' '.repeat(tabSize) : '\t');
237 return [{
238 content,
239 length: content.length,
240 offset: 0
241 }];
242 }
244 > return setProperty(content, path, value, { tabSize, insertSpaces, eol });
245 > }
247 > private _formattingOptions: Required<FormattingOptions> | undefined;
248 > private get formattingOptions(): Required<FormattingOptions> {
249 > if (!this._formattingOptions) { configurationService.ts ×13
250 > let eol = OS === OperatingSystem.Linux || OS === OperatingSystem.Macintosh ? '\n' : '\r\n';
251 > const configuredEol = this.configurationService.getValue('files.eol', { overrideIdentifier: 'jsonc' });
252 > if (configuredEol && typeof configuredEol === 'string' && configuredEol !== 'auto') {
253 eol = configuredEol;
254 }
255 > this._formattingOptions = { configurationService.ts ×13
256 > eol,
257 > insertSpaces: !!this.configurationService.getValue('editor.insertSpaces', { overrideIdentifier: 'jsonc' }),
258 > tabSize: this.configurationService.getValue('editor.tabSize', { overrideIdentifier: 'jsonc' })
259 > };
260 > }
261 > return this._formattingOptions;
262 > }