Path: blob/main/src/vs/platform/configuration/common/configuration.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { assertNever } from '../../../base/common/assert.js';6import { IStringDictionary } from '../../../base/common/collections.js';7import { Event } from '../../../base/common/event.js';8import * as types from '../../../base/common/types.js';9import { URI, UriComponents } from '../../../base/common/uri.js';10import { createDecorator } from '../../instantiation/common/instantiation.js';11import { IWorkspaceFolder } from '../../workspace/common/workspace.js';1213export const IConfigurationService = createDecorator<IConfigurationService>('configurationService');1415export function isConfigurationOverrides(thing: any): thing is IConfigurationOverrides {16return thing17&& typeof thing === 'object'18&& (!thing.overrideIdentifier || typeof thing.overrideIdentifier === 'string')19&& (!thing.resource || thing.resource instanceof URI);20}2122export interface IConfigurationOverrides {23overrideIdentifier?: string | null;24resource?: URI | null;25}2627export function isConfigurationUpdateOverrides(thing: any): thing is IConfigurationUpdateOverrides {28return thing29&& typeof thing === 'object'30&& (!thing.overrideIdentifiers || Array.isArray(thing.overrideIdentifiers))31&& !thing.overrideIdentifier32&& (!thing.resource || thing.resource instanceof URI);33}3435export type IConfigurationUpdateOverrides = Omit<IConfigurationOverrides, 'overrideIdentifier'> & { overrideIdentifiers?: string[] | null };3637export const enum ConfigurationTarget {38APPLICATION = 1,39USER,40USER_LOCAL,41USER_REMOTE,42WORKSPACE,43WORKSPACE_FOLDER,44DEFAULT,45MEMORY46}47export function ConfigurationTargetToString(configurationTarget: ConfigurationTarget) {48switch (configurationTarget) {49case ConfigurationTarget.APPLICATION: return 'APPLICATION';50case ConfigurationTarget.USER: return 'USER';51case ConfigurationTarget.USER_LOCAL: return 'USER_LOCAL';52case ConfigurationTarget.USER_REMOTE: return 'USER_REMOTE';53case ConfigurationTarget.WORKSPACE: return 'WORKSPACE';54case ConfigurationTarget.WORKSPACE_FOLDER: return 'WORKSPACE_FOLDER';55case ConfigurationTarget.DEFAULT: return 'DEFAULT';56case ConfigurationTarget.MEMORY: return 'MEMORY';57}58}5960export interface IConfigurationChange {61keys: string[];62overrides: [string, string[]][];63}6465export interface IConfigurationChangeEvent {6667readonly source: ConfigurationTarget;68readonly affectedKeys: ReadonlySet<string>;69readonly change: IConfigurationChange;7071affectsConfiguration(configuration: string, overrides?: IConfigurationOverrides): boolean;72}7374export interface IInspectValue<T> {75readonly value?: T;76readonly override?: T;77readonly overrides?: { readonly identifiers: string[]; readonly value: T }[];78}7980export interface IConfigurationValue<T> {8182readonly defaultValue?: T;83readonly applicationValue?: T;84readonly userValue?: T;85readonly userLocalValue?: T;86readonly userRemoteValue?: T;87readonly workspaceValue?: T;88readonly workspaceFolderValue?: T;89readonly memoryValue?: T;90readonly policyValue?: T;91readonly value?: T;9293readonly default?: IInspectValue<T>;94readonly application?: IInspectValue<T>;95readonly user?: IInspectValue<T>;96readonly userLocal?: IInspectValue<T>;97readonly userRemote?: IInspectValue<T>;98readonly workspace?: IInspectValue<T>;99readonly workspaceFolder?: IInspectValue<T>;100readonly memory?: IInspectValue<T>;101readonly policy?: { value?: T };102103readonly overrideIdentifiers?: string[];104}105106export function getConfigValueInTarget<T>(configValue: IConfigurationValue<T>, scope: ConfigurationTarget): T | undefined {107switch (scope) {108case ConfigurationTarget.APPLICATION:109return configValue.applicationValue;110case ConfigurationTarget.USER:111return configValue.userValue;112case ConfigurationTarget.USER_LOCAL:113return configValue.userLocalValue;114case ConfigurationTarget.USER_REMOTE:115return configValue.userRemoteValue;116case ConfigurationTarget.WORKSPACE:117return configValue.workspaceValue;118case ConfigurationTarget.WORKSPACE_FOLDER:119return configValue.workspaceFolderValue;120case ConfigurationTarget.DEFAULT:121return configValue.defaultValue;122case ConfigurationTarget.MEMORY:123return configValue.memoryValue;124default:125assertNever(scope);126}127}128129export function isConfigured<T>(configValue: IConfigurationValue<T>): configValue is IConfigurationValue<T> & { value: T } {130return configValue.applicationValue !== undefined ||131configValue.userValue !== undefined ||132configValue.userLocalValue !== undefined ||133configValue.userRemoteValue !== undefined ||134configValue.workspaceValue !== undefined ||135configValue.workspaceFolderValue !== undefined;136}137138export interface IConfigurationUpdateOptions {139/**140* If `true`, do not notifies the error to user by showing the message box. Default is `false`.141*/142donotNotifyError?: boolean;143/**144* How to handle dirty file when updating the configuration.145*/146handleDirtyFile?: 'save' | 'revert';147}148149export interface IConfigurationService {150readonly _serviceBrand: undefined;151152onDidChangeConfiguration: Event<IConfigurationChangeEvent>;153154getConfigurationData(): IConfigurationData | null;155156/**157* Fetches the value of the section for the given overrides.158* Value can be of native type or an object keyed off the section name.159*160* @param section - Section of the configuration. Can be `null` or `undefined`.161* @param overrides - Overrides that has to be applied while fetching162*163*/164getValue<T>(): T;165getValue<T>(section: string): T;166getValue<T>(overrides: IConfigurationOverrides): T;167getValue<T>(section: string, overrides: IConfigurationOverrides): T;168169/**170* Update a configuration value.171*172* Use `target` to update the configuration in a specific `ConfigurationTarget`.173*174* Use `overrides` to update the configuration for a resource or for override identifiers or both.175*176* Passing a resource through overrides will update the configuration in the workspace folder containing that resource.177*178* *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.179*180* *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.181*182* Use `donotNotifyError` and set it to `true` to surpresss errors.183*184* @param key setting to be updated185* @param value The new value186*/187updateValue(key: string, value: any): Promise<void>;188updateValue(key: string, value: any, target: ConfigurationTarget): Promise<void>;189updateValue(key: string, value: any, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides): Promise<void>;190updateValue(key: string, value: any, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, target: ConfigurationTarget, options?: IConfigurationUpdateOptions): Promise<void>;191192inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<Readonly<T>>;193194reloadConfiguration(target?: ConfigurationTarget | IWorkspaceFolder): Promise<void>;195196keys(): {197default: string[];198user: string[];199workspace: string[];200workspaceFolder: string[];201memory?: string[];202};203}204205export interface IConfigurationModel {206contents: any;207keys: string[];208overrides: IOverrides[];209raw?: IStringDictionary<any>;210}211212export interface IOverrides {213keys: string[];214contents: any;215identifiers: string[];216}217218export interface IConfigurationData {219defaults: IConfigurationModel;220policy: IConfigurationModel;221application: IConfigurationModel;222userLocal: IConfigurationModel;223userRemote: IConfigurationModel;224workspace: IConfigurationModel;225folders: [UriComponents, IConfigurationModel][];226}227228export interface IConfigurationCompareResult {229added: string[];230removed: string[];231updated: string[];232overrides: [string, string[]][];233}234235export function toValuesTree(properties: { [qualifiedKey: string]: any }, conflictReporter: (message: string) => void): any {236const root = Object.create(null);237238for (const key in properties) {239addToValueTree(root, key, properties[key], conflictReporter);240}241242return root;243}244245export function addToValueTree(settingsTreeRoot: any, key: string, value: any, conflictReporter: (message: string) => void): void {246const segments = key.split('.');247const last = segments.pop()!;248249let curr = settingsTreeRoot;250for (let i = 0; i < segments.length; i++) {251const s = segments[i];252let obj = curr[s];253switch (typeof obj) {254case 'undefined':255obj = curr[s] = Object.create(null);256break;257case 'object':258if (obj === null) {259conflictReporter(`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is null`);260return;261}262break;263default:264conflictReporter(`Ignoring ${key} as ${segments.slice(0, i + 1).join('.')} is ${JSON.stringify(obj)}`);265return;266}267curr = obj;268}269270if (typeof curr === 'object' && curr !== null) {271try {272curr[last] = value; // workaround https://github.com/microsoft/vscode/issues/13606273} catch (e) {274conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);275}276} else {277conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);278}279}280281export function removeFromValueTree(valueTree: any, key: string): void {282const segments = key.split('.');283doRemoveFromValueTree(valueTree, segments);284}285286function doRemoveFromValueTree(valueTree: any, segments: string[]): void {287if (!valueTree) {288return;289}290291const first = segments.shift()!;292if (segments.length === 0) {293// Reached last segment294delete valueTree[first];295return;296}297298if (Object.keys(valueTree).indexOf(first) !== -1) {299const value = valueTree[first];300if (typeof value === 'object' && !Array.isArray(value)) {301doRemoveFromValueTree(value, segments);302if (Object.keys(value).length === 0) {303delete valueTree[first];304}305}306}307}308309/**310* A helper function to get the configuration value with a specific settings path (e.g. config.some.setting)311*/312export function getConfigurationValue<T>(config: any, settingPath: string): T | undefined;313export function getConfigurationValue<T>(config: any, settingPath: string, defaultValue: T): T;314export function getConfigurationValue<T>(config: any, settingPath: string, defaultValue?: T): T | undefined {315function accessSetting(config: any, path: string[]): any {316let current = config;317for (const component of path) {318if (typeof current !== 'object' || current === null) {319return undefined;320}321current = current[component];322}323return <T>current;324}325326const path = settingPath.split('.');327const result = accessSetting(config, path);328329return typeof result === 'undefined' ? defaultValue : result;330}331332export function merge(base: any, add: any, overwrite: boolean): void {333Object.keys(add).forEach(key => {334if (key !== '__proto__') {335if (key in base) {336if (types.isObject(base[key]) && types.isObject(add[key])) {337merge(base[key], add[key], overwrite);338} else if (overwrite) {339base[key] = add[key];340}341} else {342base[key] = add[key];343}344}345});346}347348export function getLanguageTagSettingPlainKey(settingKey: string) {349return settingKey350.replace(/^\[/, '')351.replace(/]$/g, '')352.replace(/\]\[/g, ', ');353}354355356