src/vs/workbench/common/configuration.ts

305 LOC · 130 covered · 175 uncovered · 14 ranges · 204 concepts · 1 introducers · 79 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 > /*--------------------------------------------------------------------------------------------- configuration.ts ×14
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 { localize } from '../../nls.js';
7 > import { ConfigurationScope, IConfigurationNode, IConfigurationRegistry, Extensions as ConfigurationExtensions } from '../../platform/configuration/common/configurationRegistry.js';
8 > import { Registry } from '../../platform/registry/common/platform.js';
9 > import { IWorkbenchContribution } from './contributions.js';
10 > import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from '../../platform/workspace/common/workspace.js';
11 > import { ConfigurationTarget, IConfigurationService, IConfigurationValue, IInspectValue } from '../../platform/configuration/common/configuration.js';
12 > import { Disposable } from '../../base/common/lifecycle.js';
13 > import { Emitter } from '../../base/common/event.js';
14 > import { IRemoteAgentService } from '../services/remote/common/remoteAgentService.js';
15 > import { OperatingSystem, isWindows } from '../../base/common/platform.js';
16 > import { URI } from '../../base/common/uri.js';
17 > import { equals } from '../../base/common/objects.js';
18 > import { DeferredPromise } from '../../base/common/async.js';
19 > import { IUserDataProfile, IUserDataProfilesService } from '../../platform/userDataProfile/common/userDataProfile.js';
20 >
21 > export const applicationConfigurationNodeBase = Object.freeze<IConfigurationNode>({
22 > 'id': 'application',
23 > 'order': 100,
24 > 'title': localize('applicationConfigurationTitle', "Application"),
25 > 'type': 'object'
26 > });
27 >
28 > export const workbenchConfigurationNodeBase = Object.freeze<IConfigurationNode>({
29 > 'id': 'workbench',
30 > 'order': 7,
31 > 'title': localize('workbenchConfigurationTitle', "Workbench"),
32 > 'type': 'object',
33 > });
34 >
35 > export const securityConfigurationNodeBase = Object.freeze<IConfigurationNode>({
36 > 'id': 'security',
37 > 'scope': ConfigurationScope.APPLICATION,
38 > 'title': localize('securityConfigurationTitle', "Security"),
39 > 'type': 'object',
40 > 'order': 7
41 > });
42 >
43 > export const problemsConfigurationNodeBase = Object.freeze<IConfigurationNode>({
44 > 'id': 'problems',
45 > 'title': localize('problemsConfigurationTitle', "Problems"),
46 > 'type': 'object',
47 > 'order': 101
48 > });
49 >
50 > export const windowConfigurationNodeBase = Object.freeze<IConfigurationNode>({
51 > 'id': 'window',
52 > 'order': 8,
53 > 'title': localize('windowConfigurationTitle', "Window"),
54 > 'type': 'object',
55 > });
56 >
57 > export const Extensions = {
58 > ConfigurationMigration: 'base.contributions.configuration.migration'
59 > };
60 >
61 > type ConfigurationValue = { value: unknown | undefined /* Remove */ };
62 > export type ConfigurationKeyValuePairs = [string, ConfigurationValue][];
63 > // eslint-disable-next-line @typescript-eslint/no-explicit-any
64 > export type ConfigurationMigrationFn = (value: any, valueAccessor: (key: string) => any) => ConfigurationValue | ConfigurationKeyValuePairs | Promise<ConfigurationValue | ConfigurationKeyValuePairs>;
65 > export type ConfigurationMigration = { key: string; migrateFn: ConfigurationMigrationFn };
66 >
67 > export interface IConfigurationMigrationRegistry {
68 > registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void;
69 > }
70 >
71 > class ConfigurationMigrationRegistry implements IConfigurationMigrationRegistry {
72 >
73 > readonly migrations: ConfigurationMigration[] = [];
74 >
75 > private readonly _onDidRegisterConfigurationMigrations = new Emitter<ConfigurationMigration[]>();
76 > readonly onDidRegisterConfigurationMigration = this._onDidRegisterConfigurationMigrations.event;
77 >
78 > registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void {
79 this.migrations.push(...configurationMigrations);
80 }
82 > }
83 >
84 > const configurationMigrationRegistry = new ConfigurationMigrationRegistry();
85 > Registry.add(Extensions.ConfigurationMigration, configurationMigrationRegistry);
86 >
87 > export class ConfigurationMigrationWorkbenchContribution extends Disposable implements IWorkbenchContribution {
88 >
89 > static readonly ID = 'workbench.contrib.configurationMigration';
90 >
91 > constructor(
92 @IConfigurationService private readonly configurationService: IConfigurationService,
93 @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
94 ) {
95 super();
96 this._register(this.workspaceService.onDidChangeWorkspaceFolders(async (e) => {
97 for (const folder of e.added) {
98 await this.migrateConfigurationsForFolder(folder, configurationMigrationRegistry.migrations);
99 }
100 }));
101 this.migrateConfigurations(configurationMigrationRegistry.migrations);
102 this._register(configurationMigrationRegistry.onDidRegisterConfigurationMigration(migration => this.migrateConfigurations(migration)));
103 }
105 > private async migrateConfigurations(migrations: ConfigurationMigration[]): Promise<void> {
106 await this.migrateConfigurationsForFolder(undefined, migrations);
107 for (const folder of this.workspaceService.getWorkspace().folders) {
108 await this.migrateConfigurationsForFolder(folder, migrations);
109 }
110 }
112 > private async migrateConfigurationsForFolder(folder: IWorkspaceFolder | undefined, migrations: ConfigurationMigration[]): Promise<void> {
113 await Promise.all([migrations.map(migration => this.migrateConfigurationsForFolderAndOverride(migration, folder?.uri))]);
114 }
116 > private async migrateConfigurationsForFolderAndOverride(migration: ConfigurationMigration, resource?: URI): Promise<void> {
117 const inspectData = this.configurationService.inspect(migration.key, { resource });
118
119 const targetPairs: [keyof IConfigurationValue<unknown>, ConfigurationTarget][] = this.workspaceService.getWorkbenchState() === WorkbenchState.WORKSPACE ? [
120 ['user', ConfigurationTarget.USER],
121 ['userLocal', ConfigurationTarget.USER_LOCAL],
122 ['userRemote', ConfigurationTarget.USER_REMOTE],
123 ['workspace', ConfigurationTarget.WORKSPACE],
124 ['workspaceFolder', ConfigurationTarget.WORKSPACE_FOLDER],
125 ] : [
126 ['user', ConfigurationTarget.USER],
127 ['userLocal', ConfigurationTarget.USER_LOCAL],
128 ['userRemote', ConfigurationTarget.USER_REMOTE],
129 ['workspace', ConfigurationTarget.WORKSPACE],
130 ];
131 for (const [dataKey, target] of targetPairs) {
132 const inspectValue = inspectData[dataKey] as IInspectValue<unknown> | undefined;
133 if (!inspectValue) {
134 continue;
135 }
136
137 const migrationValues: [[string, ConfigurationValue], string[]][] = [];
138
139 if (inspectValue.value !== undefined) {
140 const keyValuePairs = await this.runMigration(migration, dataKey, inspectValue.value, resource, undefined);
141 for (const keyValuePair of keyValuePairs ?? []) {
142 migrationValues.push([keyValuePair, []]);
143 }
144 }
145
146 for (const { identifiers, value } of inspectValue.overrides ?? []) {
147 if (value !== undefined) {
148 const keyValuePairs = await this.runMigration(migration, dataKey, value, resource, identifiers);
149 for (const keyValuePair of keyValuePairs ?? []) {
150 migrationValues.push([keyValuePair, identifiers]);
151 }
152 }
153 }
154
155 if (migrationValues.length) {
156 // apply migrations
157 await Promise.allSettled(migrationValues.map(async ([[key, value], overrideIdentifiers]) =>
158 this.configurationService.updateValue(key, value.value, { resource, overrideIdentifiers }, target)));
159 }
160 }
161 }
163 > private async runMigration(migration: ConfigurationMigration, dataKey: keyof IConfigurationValue<unknown>, value: unknown, resource: URI | undefined, overrideIdentifiers: string[] | undefined): Promise<ConfigurationKeyValuePairs | undefined> {
164 const valueAccessor = (key: string) => {
165 const inspectData = this.configurationService.inspect(key, { resource });
166 const inspectValue = inspectData[dataKey] as IInspectValue<unknown> | undefined;
167 if (!inspectValue) {
168 return undefined;
169 }
170 if (!overrideIdentifiers) {
171 return inspectValue.value;
172 }
173 return inspectValue.overrides?.find(({ identifiers }) => equals(identifiers, overrideIdentifiers))?.value;
174 };
175 const result = await migration.migrateFn(value, valueAccessor);
176 return Array.isArray(result) ? result : [[migration.key, result]];
177 }
179 >
180 > export class DynamicWorkbenchSecurityConfiguration extends Disposable implements IWorkbenchContribution {
181 >
182 > static readonly ID = 'workbench.contrib.dynamicWorkbenchSecurityConfiguration';
183 >
184 > private readonly _ready = new DeferredPromise<void>();
185 > readonly ready = this._ready.p;
186 >
187 > constructor(
188 @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService
189 ) {
190 super();
191
192 this.create();
193 }
195 > private async create(): Promise<void> {
196 try {
197 await this.doCreate();
198 } finally {
199 this._ready.complete();
200 }
201 }
203 > private async doCreate(): Promise<void> {
204 if (!isWindows) {
205 const remoteEnvironment = await this.remoteAgentService.getEnvironment();
206 if (remoteEnvironment?.os !== OperatingSystem.Windows) {
207 return;
208 }
209 }
210
211 // Windows: UNC allow list security configuration
212 const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
213 registry.registerConfiguration({
214 ...securityConfigurationNodeBase,
215 'properties': {
216 'security.allowedUNCHosts': {
217 'type': 'array',
218 'items': {
219 'type': 'string',
220 'pattern': '^[^\\\\]+$',
221 'patternErrorMessage': localize('security.allowedUNCHosts.patternErrorMessage', 'UNC host names must not contain backslashes.')
222 },
223 'default': [],
224 'markdownDescription': localize('security.allowedUNCHosts', 'A set of UNC host names (without leading or trailing backslash, for example `192.168.0.1` or `my-server`) to allow without user confirmation. If a UNC host is being accessed that is not allowed via this setting or has not been acknowledged via user confirmation, an error will occur and the operation stopped. A restart is required when changing this setting. Find out more about this setting at https://aka.ms/vscode-windows-unc.'),
225 'scope': ConfigurationScope.APPLICATION_MACHINE
226 },
227 'security.restrictUNCAccess': {
228 'type': 'boolean',
229 'default': true,
230 'markdownDescription': localize('security.restrictUNCAccess', 'If enabled, only allows access to UNC host names that are allowed by the `#security.allowedUNCHosts#` setting or after user confirmation. Find out more about this setting at https://aka.ms/vscode-windows-unc.'),
231 'scope': ConfigurationScope.APPLICATION_MACHINE
232 }
233 }
234 });
235 }
237 >
238 > export const CONFIG_NEW_WINDOW_PROFILE = 'window.newWindowProfile';
239 >
240 > export class DynamicWindowConfiguration extends Disposable implements IWorkbenchContribution {
241 >
242 > static readonly ID = 'workbench.contrib.dynamicWindowConfiguration';
243 >
244 > private configurationNode: IConfigurationNode | undefined;
245 > private newWindowProfile: IUserDataProfile | undefined;
246 >
247 > constructor(
248 @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService,
249 @IConfigurationService private readonly configurationService: IConfigurationService,
250 ) {
251 super();
252 this.registerNewWindowProfileConfiguration();
253 this._register(this.userDataProfilesService.onDidChangeProfiles((e) => this.registerNewWindowProfileConfiguration()));
254
255 this.setNewWindowProfile();
256 this.checkAndResetNewWindowProfileConfig();
257
258 this._register(configurationService.onDidChangeConfiguration(e => {
259 if (e.source !== ConfigurationTarget.DEFAULT && e.affectsConfiguration(CONFIG_NEW_WINDOW_PROFILE)) {
260 this.setNewWindowProfile();
261 }
262 }));
263 this._register(this.userDataProfilesService.onDidChangeProfiles(() => this.checkAndResetNewWindowProfileConfig()));
264 }
266 > private registerNewWindowProfileConfiguration(): void {
267 const registry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
268 const configurationNode: IConfigurationNode = {
269 ...windowConfigurationNodeBase,
270 'properties': {
271 [CONFIG_NEW_WINDOW_PROFILE]: {
272 'type': ['string', 'null'],
273 'default': null,
274 'enum': [...this.userDataProfilesService.profiles.map(profile => profile.name), null],
275 'enumItemLabels': [...this.userDataProfilesService.profiles.map(() => ''), localize('active window', "Active Window")],
276 'description': localize('newWindowProfile', "Specifies the profile to use when opening a new window. If a profile name is provided, the new window will use that profile. If no profile name is provided, the new window will use the profile of the active window or the Default profile if no active window exists."),
277 'scope': ConfigurationScope.APPLICATION,
278 }
279 }
280 };
281 if (this.configurationNode) {
282 registry.updateConfigurations({ add: [configurationNode], remove: [this.configurationNode] });
283 } else {
284 registry.registerConfiguration(configurationNode);
285 }
286 this.configurationNode = configurationNode;
287 }
289 > private setNewWindowProfile(): void {
290 const newWindowProfileName = this.configurationService.getValue(CONFIG_NEW_WINDOW_PROFILE);
291 this.newWindowProfile = newWindowProfileName ? this.userDataProfilesService.profiles.find(profile => profile.name === newWindowProfileName) : undefined;
292 }
294 > private checkAndResetNewWindowProfileConfig(): void {
295 const newWindowProfileName = this.configurationService.getValue(CONFIG_NEW_WINDOW_PROFILE);
296 if (!newWindowProfileName) {
297 return;
298 }
299 const profile = this.newWindowProfile ? this.userDataProfilesService.profiles.find(profile => profile.id === this.newWindowProfile!.id) : undefined;
300 if (newWindowProfileName === profile?.name) {
301 return;
302 }
303 this.configurationService.updateValue(CONFIG_NEW_WINDOW_PROFILE, profile?.name);
304 }