|
5 | 5 |
|
6 | 6 | import { localize } from 'vs/nls';
|
7 | 7 | import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
|
| 8 | +import { Registry } from 'vs/platform/registry/common/platform'; |
| 9 | +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; |
| 10 | +import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; |
| 11 | +import { ConfigurationTarget, IConfigurationOverrides, IConfigurationService, IConfigurationValue } from 'vs/platform/configuration/common/configuration'; |
| 12 | +import { Disposable } from 'vs/base/common/lifecycle'; |
| 13 | +import { Emitter } from 'vs/base/common/event'; |
8 | 14 |
|
9 | 15 | export const workbenchConfigurationNodeBase = Object.freeze<IConfigurationNode>({
|
10 | 16 | 'id': 'workbench',
|
11 | 17 | 'order': 7,
|
12 | 18 | 'title': localize('workbenchConfigurationTitle', "Workbench"),
|
13 | 19 | 'type': 'object',
|
14 | 20 | });
|
| 21 | + |
| 22 | +export const Extensions = { |
| 23 | + ConfigurationMigration: 'base.contributions.configuration.migration' |
| 24 | +}; |
| 25 | + |
| 26 | +export type ConfigurationValue = { value: any | undefined /* Remove */ }; |
| 27 | +export type ConfigurationKeyValuePairs = [string, ConfigurationValue][]; |
| 28 | +export type ConfigurationMigrationFn = (value: any, valueAccessor: (key: string) => any) => ConfigurationValue | ConfigurationKeyValuePairs | Promise<ConfigurationValue | ConfigurationKeyValuePairs>; |
| 29 | +export type ConfigurationMigration = { key: string; migrateFn: ConfigurationMigrationFn }; |
| 30 | + |
| 31 | +export interface IConfigurationMigrationRegistry { |
| 32 | + registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void; |
| 33 | +} |
| 34 | + |
| 35 | +class ConfigurationMigrationRegistry implements IConfigurationMigrationRegistry { |
| 36 | + |
| 37 | + readonly migrations: ConfigurationMigration[] = []; |
| 38 | + |
| 39 | + private readonly _onDidRegisterConfigurationMigrations = new Emitter<ConfigurationMigration[]>(); |
| 40 | + readonly onDidRegisterConfigurationMigration = this._onDidRegisterConfigurationMigrations.event; |
| 41 | + |
| 42 | + registerConfigurationMigrations(configurationMigrations: ConfigurationMigration[]): void { |
| 43 | + this.migrations.push(...configurationMigrations); |
| 44 | + } |
| 45 | + |
| 46 | +} |
| 47 | + |
| 48 | +const configurationMigrationRegistry = new ConfigurationMigrationRegistry(); |
| 49 | +Registry.add(Extensions.ConfigurationMigration, configurationMigrationRegistry); |
| 50 | + |
| 51 | +export class ConfigurationMigrationWorkbenchContribution extends Disposable implements IWorkbenchContribution { |
| 52 | + |
| 53 | + constructor( |
| 54 | + @IConfigurationService private readonly configurationService: IConfigurationService, |
| 55 | + @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, |
| 56 | + ) { |
| 57 | + super(); |
| 58 | + this._register(this.workspaceService.onDidChangeWorkspaceFolders(async (e) => { |
| 59 | + for (const folder of e.added) { |
| 60 | + await this.migrateConfigurationsForFolder(folder, configurationMigrationRegistry.migrations); |
| 61 | + } |
| 62 | + })); |
| 63 | + this.migrateConfigurations(configurationMigrationRegistry.migrations); |
| 64 | + this._register(configurationMigrationRegistry.onDidRegisterConfigurationMigration(migration => this.migrateConfigurations(migration))); |
| 65 | + } |
| 66 | + |
| 67 | + private async migrateConfigurations(migrations: ConfigurationMigration[]): Promise<void> { |
| 68 | + await this.migrateConfigurationsForFolder(undefined, migrations); |
| 69 | + for (const folder of this.workspaceService.getWorkspace().folders) { |
| 70 | + await this.migrateConfigurationsForFolder(folder, migrations); |
| 71 | + } |
| 72 | + } |
| 73 | + |
| 74 | + private async migrateConfigurationsForFolder(folder: IWorkspaceFolder | undefined, migrations: ConfigurationMigration[]): Promise<void> { |
| 75 | + await Promise.all(migrations.map(migration => this.migrateConfigurationsForFolderAndOverride(migration, { resource: folder?.uri }))); |
| 76 | + } |
| 77 | + |
| 78 | + private async migrateConfigurationsForFolderAndOverride(migration: ConfigurationMigration, overrides: IConfigurationOverrides): Promise<void> { |
| 79 | + const data = this.configurationService.inspect(migration.key, overrides); |
| 80 | + |
| 81 | + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userValue', ConfigurationTarget.USER); |
| 82 | + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userLocalValue', ConfigurationTarget.USER_LOCAL); |
| 83 | + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'userRemoteValue', ConfigurationTarget.USER_REMOTE); |
| 84 | + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'workspaceFolderValue', ConfigurationTarget.WORKSPACE_FOLDER); |
| 85 | + await this.migrateConfigurationForFolderOverrideAndTarget(migration, overrides, data, 'workspaceValue', ConfigurationTarget.WORKSPACE); |
| 86 | + |
| 87 | + if (typeof overrides.overrideIdentifier === 'undefined' && typeof data.overrideIdentifiers !== 'undefined') { |
| 88 | + for (const overrideIdentifier of data.overrideIdentifiers) { |
| 89 | + await this.migrateConfigurationsForFolderAndOverride(migration, { resource: overrides.resource, overrideIdentifier }); |
| 90 | + } |
| 91 | + } |
| 92 | + } |
| 93 | + |
| 94 | + private async migrateConfigurationForFolderOverrideAndTarget(migration: ConfigurationMigration, overrides: IConfigurationOverrides, data: IConfigurationValue<any>, dataKey: keyof IConfigurationValue<any>, target: ConfigurationTarget): Promise<void> { |
| 95 | + const value = data[dataKey]; |
| 96 | + if (typeof value === 'undefined') { |
| 97 | + return; |
| 98 | + } |
| 99 | + |
| 100 | + const valueAccessor = (key: string) => this.configurationService.inspect(key, overrides)[dataKey]; |
| 101 | + const result = await migration.migrateFn(value, valueAccessor); |
| 102 | + const keyValuePairs: ConfigurationKeyValuePairs = Array.isArray(result) ? result : [[migration.key, result]]; |
| 103 | + await Promise.allSettled(keyValuePairs.map(async ([key, value]) => this.configurationService.updateValue(key, value.value, overrides, target))); |
| 104 | + } |
| 105 | +} |
0 commit comments