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