Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/platform/configuration/common/configuration.ts
5259 views
1
/*---------------------------------------------------------------------------------------------
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 { assertNever } from '../../../base/common/assert.js';
7
import { IStringDictionary } from '../../../base/common/collections.js';
8
import { Event } from '../../../base/common/event.js';
9
import * as types from '../../../base/common/types.js';
10
import { URI, UriComponents } from '../../../base/common/uri.js';
11
import { createDecorator } from '../../instantiation/common/instantiation.js';
12
import { IWorkspaceFolder } from '../../workspace/common/workspace.js';
13
14
export const IConfigurationService = createDecorator<IConfigurationService>('configurationService');
15
16
export function isConfigurationOverrides(obj: unknown): obj is IConfigurationOverrides {
17
const thing = obj as IConfigurationOverrides;
18
return thing
19
&& typeof thing === 'object'
20
&& (!thing.overrideIdentifier || typeof thing.overrideIdentifier === 'string')
21
&& (!thing.resource || thing.resource instanceof URI);
22
}
23
24
export interface IConfigurationOverrides {
25
overrideIdentifier?: string | null;
26
resource?: URI | null;
27
}
28
29
export function isConfigurationUpdateOverrides(obj: unknown): obj is IConfigurationUpdateOverrides {
30
const thing = obj as IConfigurationUpdateOverrides | IConfigurationOverrides;
31
return thing
32
&& typeof thing === 'object'
33
&& (!(thing as IConfigurationUpdateOverrides).overrideIdentifiers || Array.isArray((thing as IConfigurationUpdateOverrides).overrideIdentifiers))
34
&& !(thing as IConfigurationOverrides).overrideIdentifier
35
&& (!thing.resource || thing.resource instanceof URI);
36
}
37
38
export type IConfigurationUpdateOverrides = Omit<IConfigurationOverrides, 'overrideIdentifier'> & { overrideIdentifiers?: string[] | null };
39
40
export const enum ConfigurationTarget {
41
APPLICATION = 1,
42
USER,
43
USER_LOCAL,
44
USER_REMOTE,
45
WORKSPACE,
46
WORKSPACE_FOLDER,
47
DEFAULT,
48
MEMORY
49
}
50
export function ConfigurationTargetToString(configurationTarget: ConfigurationTarget) {
51
switch (configurationTarget) {
52
case ConfigurationTarget.APPLICATION: return 'APPLICATION';
53
case ConfigurationTarget.USER: return 'USER';
54
case ConfigurationTarget.USER_LOCAL: return 'USER_LOCAL';
55
case ConfigurationTarget.USER_REMOTE: return 'USER_REMOTE';
56
case ConfigurationTarget.WORKSPACE: return 'WORKSPACE';
57
case ConfigurationTarget.WORKSPACE_FOLDER: return 'WORKSPACE_FOLDER';
58
case ConfigurationTarget.DEFAULT: return 'DEFAULT';
59
case ConfigurationTarget.MEMORY: return 'MEMORY';
60
}
61
}
62
63
export interface IConfigurationChange {
64
keys: string[];
65
overrides: [string, string[]][];
66
}
67
68
export interface IConfigurationChangeEvent {
69
70
readonly source: ConfigurationTarget;
71
readonly affectedKeys: ReadonlySet<string>;
72
readonly change: IConfigurationChange;
73
74
affectsConfiguration(configuration: string, overrides?: IConfigurationOverrides): boolean;
75
}
76
77
export interface IInspectValue<T> {
78
readonly value?: T;
79
readonly override?: T;
80
readonly overrides?: { readonly identifiers: string[]; readonly value: T }[];
81
}
82
83
export interface IConfigurationValue<T> {
84
85
readonly defaultValue?: T;
86
readonly applicationValue?: T;
87
readonly userValue?: T;
88
readonly userLocalValue?: T;
89
readonly userRemoteValue?: T;
90
readonly workspaceValue?: T;
91
readonly workspaceFolderValue?: T;
92
readonly memoryValue?: T;
93
readonly policyValue?: T;
94
readonly value?: T;
95
96
readonly default?: IInspectValue<T>;
97
readonly application?: IInspectValue<T>;
98
readonly user?: IInspectValue<T>;
99
readonly userLocal?: IInspectValue<T>;
100
readonly userRemote?: IInspectValue<T>;
101
readonly workspace?: IInspectValue<T>;
102
readonly workspaceFolder?: IInspectValue<T>;
103
readonly memory?: IInspectValue<T>;
104
readonly policy?: { value?: T };
105
106
readonly overrideIdentifiers?: string[];
107
}
108
109
export function getConfigValueInTarget<T>(configValue: IConfigurationValue<T>, scope: ConfigurationTarget): T | undefined {
110
switch (scope) {
111
case ConfigurationTarget.APPLICATION:
112
return configValue.applicationValue;
113
case ConfigurationTarget.USER:
114
return configValue.userValue;
115
case ConfigurationTarget.USER_LOCAL:
116
return configValue.userLocalValue;
117
case ConfigurationTarget.USER_REMOTE:
118
return configValue.userRemoteValue;
119
case ConfigurationTarget.WORKSPACE:
120
return configValue.workspaceValue;
121
case ConfigurationTarget.WORKSPACE_FOLDER:
122
return configValue.workspaceFolderValue;
123
case ConfigurationTarget.DEFAULT:
124
return configValue.defaultValue;
125
case ConfigurationTarget.MEMORY:
126
return configValue.memoryValue;
127
default:
128
assertNever(scope);
129
}
130
}
131
132
export function isConfigured<T>(configValue: IConfigurationValue<T>): configValue is IConfigurationValue<T> & { value: T } {
133
return configValue.applicationValue !== undefined ||
134
configValue.userValue !== undefined ||
135
configValue.userLocalValue !== undefined ||
136
configValue.userRemoteValue !== undefined ||
137
configValue.workspaceValue !== undefined ||
138
configValue.workspaceFolderValue !== undefined;
139
}
140
141
export interface IConfigurationUpdateOptions {
142
/**
143
* If `true`, do not notifies the error to user by showing the message box. Default is `false`.
144
*/
145
donotNotifyError?: boolean;
146
/**
147
* How to handle dirty file when updating the configuration.
148
*/
149
handleDirtyFile?: 'save' | 'revert';
150
}
151
152
export interface IConfigurationService {
153
readonly _serviceBrand: undefined;
154
155
readonly onDidChangeConfiguration: Event<IConfigurationChangeEvent>;
156
157
getConfigurationData(): IConfigurationData | null;
158
159
/**
160
* Fetches the value of the section for the given overrides.
161
* Value can be of native type or an object keyed off the section name.
162
*
163
* @param section - Section of the configuration. Can be `null` or `undefined`.
164
* @param overrides - Overrides that has to be applied while fetching
165
*
166
*/
167
getValue<T>(): T;
168
getValue<T>(section: string): T;
169
getValue<T>(overrides: IConfigurationOverrides): T;
170
getValue<T>(section: string, overrides: IConfigurationOverrides): T;
171
172
/**
173
* Update a configuration value.
174
*
175
* Use `target` to update the configuration in a specific `ConfigurationTarget`.
176
*
177
* Use `overrides` to update the configuration for a resource or for override identifiers or both.
178
*
179
* Passing a resource through overrides will update the configuration in the workspace folder containing that resource.
180
*
181
* *Note 1:* Updating configuration to a default value will remove the configuration from the requested target. If not target is passed, it will be removed from all writeable targets.
182
*
183
* *Note 2:* Use `undefined` value to remove the configuration from the given target. If not target is passed, it will be removed from all writeable targets.
184
*
185
* Use `donotNotifyError` and set it to `true` to surpresss errors.
186
*
187
* @param key setting to be updated
188
* @param value The new value
189
*/
190
updateValue(key: string, value: unknown): Promise<void>;
191
updateValue(key: string, value: unknown, target: ConfigurationTarget): Promise<void>;
192
updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides): Promise<void>;
193
updateValue(key: string, value: unknown, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, target: ConfigurationTarget, options?: IConfigurationUpdateOptions): Promise<void>;
194
195
inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<Readonly<T>>;
196
197
reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise<void>;
198
199
keys(): {
200
default: string[];
201
policy: string[];
202
user: string[];
203
workspace: string[];
204
workspaceFolder: string[];
205
memory?: string[];
206
};
207
}
208
209
export interface IConfigurationModel {
210
contents: IStringDictionary<unknown>;
211
keys: string[];
212
overrides: IOverrides[];
213
raw?: ReadonlyArray<IStringDictionary<unknown>> | IStringDictionary<unknown>;
214
}
215
216
export interface IOverrides {
217
keys: string[];
218
contents: IStringDictionary<unknown>;
219
identifiers: string[];
220
}
221
222
export interface IConfigurationData {
223
defaults: IConfigurationModel;
224
policy: IConfigurationModel;
225
application: IConfigurationModel;
226
userLocal: IConfigurationModel;
227
userRemote: IConfigurationModel;
228
workspace: IConfigurationModel;
229
folders: [UriComponents, IConfigurationModel][];
230
}
231
232
export interface IConfigurationCompareResult {
233
added: string[];
234
removed: string[];
235
updated: string[];
236
overrides: [string, string[]][];
237
}
238
239
export function toValuesTree(properties: IStringDictionary<unknown>, conflictReporter: (message: string) => void): IStringDictionary<unknown> {
240
const root = Object.create(null);
241
242
for (const key in properties) {
243
addToValueTree(root, key, properties[key], conflictReporter);
244
}
245
246
return root;
247
}
248
249
export function addToValueTree(settingsTreeRoot: IStringDictionary<unknown>, key: string, value: unknown, conflictReporter: (message: string) => void): void {
250
const segments = key.split('.');
251
const last = segments.pop()!;
252
253
let curr: IStringDictionary<unknown> = settingsTreeRoot;
254
for (let i = 0; i < segments.length; i++) {
255
const s = segments[i];
256
let obj = curr[s];
257
switch (typeof obj) {
258
case 'undefined':
259
obj = curr[s] = Object.create(null);
260
break;
261
case 'object':
262
if (obj === null) {
263
conflictReporter(`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is null`);
264
return;
265
}
266
break;
267
default:
268
conflictReporter(`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is ${JSON.stringify(obj)}`);
269
return;
270
}
271
curr = obj as IStringDictionary<unknown>;
272
}
273
274
if (typeof curr === 'object' && curr !== null) {
275
try {
276
(curr as IStringDictionary<unknown>)[last] = value; // workaround https://github.com/microsoft/vscode/issues/13606
277
} catch (e) {
278
conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);
279
}
280
} else {
281
conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);
282
}
283
}
284
285
export function removeFromValueTree(valueTree: IStringDictionary<unknown>, key: string): void {
286
const segments = key.split('.');
287
doRemoveFromValueTree(valueTree, segments);
288
}
289
290
function doRemoveFromValueTree(valueTree: IStringDictionary<unknown> | unknown, segments: string[]): void {
291
if (!valueTree) {
292
return;
293
}
294
295
const valueTreeRecord = valueTree as IStringDictionary<unknown>;
296
const first = segments.shift()!;
297
if (segments.length === 0) {
298
// Reached last segment
299
delete valueTreeRecord[first];
300
return;
301
}
302
303
if (Object.keys(valueTreeRecord).indexOf(first) !== -1) {
304
const value = valueTreeRecord[first];
305
if (typeof value === 'object' && !Array.isArray(value)) {
306
doRemoveFromValueTree(value, segments);
307
if (Object.keys(value as object).length === 0) {
308
delete valueTreeRecord[first];
309
}
310
}
311
}
312
}
313
314
/**
315
* A helper function to get the configuration value with a specific settings path (e.g. config.some.setting)
316
*/
317
export function getConfigurationValue<T>(config: IStringDictionary<unknown>, settingPath: string): T | undefined;
318
export function getConfigurationValue<T>(config: IStringDictionary<unknown>, settingPath: string, defaultValue: T): T;
319
export function getConfigurationValue<T>(config: IStringDictionary<unknown>, settingPath: string, defaultValue?: T): T | undefined {
320
function accessSetting(config: IStringDictionary<unknown>, path: string[]): unknown {
321
let current: unknown = config;
322
for (const component of path) {
323
if (typeof current !== 'object' || current === null) {
324
return undefined;
325
}
326
current = (current as IStringDictionary<unknown>)[component];
327
}
328
return current as T;
329
}
330
331
const path = settingPath.split('.');
332
const result = accessSetting(config, path);
333
334
return typeof result === 'undefined' ? defaultValue : result as T;
335
}
336
337
export function merge(base: IStringDictionary<unknown>, add: IStringDictionary<unknown>, overwrite: boolean): void {
338
Object.keys(add).forEach(key => {
339
if (key !== '__proto__') {
340
if (key in base) {
341
if (types.isObject(base[key]) && types.isObject(add[key])) {
342
merge(base[key] as IStringDictionary<unknown>, add[key] as IStringDictionary<unknown>, overwrite);
343
} else if (overwrite) {
344
base[key] = add[key];
345
}
346
} else {
347
base[key] = add[key];
348
}
349
}
350
});
351
}
352
353
export function getLanguageTagSettingPlainKey(settingKey: string) {
354
return settingKey
355
.replace(/^\[/, '')
356
.replace(/]$/g, '')
357
.replace(/\]\[/g, ', ');
358
}
359
360