Merge vscode 1.67 (#20883)

* Fix initial build breaks from 1.67 merge (#2514)

* Update yarn lock files

* Update build scripts

* Fix tsconfig

* Build breaks

* WIP

* Update yarn lock files

* Misc breaks

* Updates to package.json

* Breaks

* Update yarn

* Fix breaks

* Breaks

* Build breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Missing file

* Breaks

* Breaks

* Breaks

* Breaks

* Breaks

* Fix several runtime breaks (#2515)

* Missing files

* Runtime breaks

* Fix proxy ordering issue

* Remove commented code

* Fix breaks with opening query editor

* Fix post merge break

* Updates related to setup build and other breaks (#2516)

* Fix bundle build issues

* Update distro

* Fix distro merge and update build JS files

* Disable pipeline steps

* Remove stats call

* Update license name

* Make new RPM dependencies a warning

* Fix extension manager version checks

* Update JS file

* Fix a few runtime breaks

* Fixes

* Fix runtime issues

* Fix build breaks

* Update notebook tests (part 1)

* Fix broken tests

* Linting errors

* Fix hygiene

* Disable lint rules

* Bump distro

* Turn off smoke tests

* Disable integration tests

* Remove failing "activate" test

* Remove failed test assertion

* Disable other broken test

* Disable query history tests

* Disable extension unit tests

* Disable failing tasks
This commit is contained in:
Karl Burtram
2022-10-19 19:13:18 -07:00
committed by GitHub
parent 33c6daaea1
commit 8a3d08f0de
3738 changed files with 192313 additions and 107208 deletions

View File

@@ -3,14 +3,10 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { IStringDictionary } from 'vs/base/common/collections';
import { Event } from 'vs/base/common/event';
import * as objects from 'vs/base/common/objects';
import * as types from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { Extensions, IConfigurationRegistry, overrideIdentifierFromKey, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
export const IConfigurationService = createDecorator<IConfigurationService>('configurationService');
@@ -27,6 +23,16 @@ export interface IConfigurationOverrides {
resource?: URI | null;
}
export function isConfigurationUpdateOverrides(thing: any): thing is IConfigurationUpdateOverrides {
return thing
&& typeof thing === 'object'
&& (!thing.overrideIdentifiers || types.isArray(thing.overrideIdentifiers))
&& !thing.overrideIdentifier
&& (!thing.resource || thing.resource instanceof URI);
}
export type IConfigurationUpdateOverrides = Omit<IConfigurationOverrides, 'overrideIdentifier'> & { overrideIdentifiers?: string[] | null };
export const enum ConfigurationTarget {
USER = 1,
USER_LOCAL,
@@ -76,13 +82,13 @@ export interface IConfigurationValue<T> {
readonly memoryValue?: T;
readonly value?: T;
readonly default?: { value?: T, override?: T };
readonly user?: { value?: T, override?: T };
readonly userLocal?: { value?: T, override?: T };
readonly userRemote?: { value?: T, override?: T };
readonly workspace?: { value?: T, override?: T };
readonly workspaceFolder?: { value?: T, override?: T };
readonly memory?: { value?: T, override?: T };
readonly default?: { value?: T; override?: T };
readonly user?: { value?: T; override?: T };
readonly userLocal?: { value?: T; override?: T };
readonly userRemote?: { value?: T; override?: T };
readonly workspace?: { value?: T; override?: T };
readonly workspaceFolder?: { value?: T; override?: T };
readonly memory?: { value?: T; override?: T };
readonly overrideIdentifiers?: string[];
}
@@ -107,10 +113,28 @@ export interface IConfigurationService {
getValue<T>(overrides: IConfigurationOverrides): T;
getValue<T>(section: string, overrides: IConfigurationOverrides): T;
/**
* Update a configuration value.
*
* Use `target` to update the configuration in a specific `ConfigurationTarget`.
*
* Use `overrides` to update the configuration for a resource or for override identifiers or both.
*
* Passing a resource through overrides will update the configuration in the workspace folder containing that resource.
*
* *Note 1:* Updating configuraiton 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.
*
* *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.
*
* Use `donotNotifyError` and set it to `true` to surpresss errors.
*
* @param key setting to be updated
* @param value The new value
*/
updateValue(key: string, value: any): Promise<void>;
updateValue(key: string, value: any, overrides: IConfigurationOverrides): Promise<void>;
updateValue(key: string, value: any, target: ConfigurationTarget): Promise<void>;
updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError?: boolean): Promise<void>;
updateValue(key: string, value: any, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides): Promise<void>;
updateValue(key: string, value: any, overrides: IConfigurationOverrides | IConfigurationUpdateOverrides, target: ConfigurationTarget, donotNotifyError?: boolean): Promise<void>;
inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<Readonly<T>>;
@@ -151,89 +175,6 @@ export interface IConfigurationCompareResult {
overrides: [string, string[]][];
}
export function compare(from: IConfigurationModel | undefined, to: IConfigurationModel | undefined): IConfigurationCompareResult {
const added = to
? from ? to.keys.filter(key => from.keys.indexOf(key) === -1) : [...to.keys]
: [];
const removed = from
? to ? from.keys.filter(key => to.keys.indexOf(key) === -1) : [...from.keys]
: [];
const updated: string[] = [];
if (to && from) {
for (const key of from.keys) {
if (to.keys.indexOf(key) !== -1) {
const value1 = getConfigurationValue(from.contents, key);
const value2 = getConfigurationValue(to.contents, key);
if (!objects.equals(value1, value2)) {
updated.push(key);
}
}
}
}
const overrides: [string, string[]][] = [];
const byOverrideIdentifier = (overrides: IOverrides[]): IStringDictionary<IOverrides> => {
const result: IStringDictionary<IOverrides> = {};
for (const override of overrides) {
for (const identifier of override.identifiers) {
result[keyFromOverrideIdentifier(identifier)] = override;
}
}
return result;
};
const toOverridesByIdentifier: IStringDictionary<IOverrides> = to ? byOverrideIdentifier(to.overrides) : {};
const fromOverridesByIdentifier: IStringDictionary<IOverrides> = from ? byOverrideIdentifier(from.overrides) : {};
if (Object.keys(toOverridesByIdentifier).length) {
for (const key of added) {
const override = toOverridesByIdentifier[key];
if (override) {
overrides.push([overrideIdentifierFromKey(key), override.keys]);
}
}
}
if (Object.keys(fromOverridesByIdentifier).length) {
for (const key of removed) {
const override = fromOverridesByIdentifier[key];
if (override) {
overrides.push([overrideIdentifierFromKey(key), override.keys]);
}
}
}
if (Object.keys(toOverridesByIdentifier).length && Object.keys(fromOverridesByIdentifier).length) {
for (const key of updated) {
const fromOverride = fromOverridesByIdentifier[key];
const toOverride = toOverridesByIdentifier[key];
if (fromOverride && toOverride) {
const result = compare({ contents: fromOverride.contents, keys: fromOverride.keys, overrides: [] }, { contents: toOverride.contents, keys: toOverride.keys, overrides: [] });
overrides.push([overrideIdentifierFromKey(key), [...result.added, ...result.removed, ...result.updated]]);
}
}
}
return { added, removed, updated, overrides };
}
export function toOverrides(raw: any, conflictReporter: (message: string) => void): IOverrides[] {
const overrides: IOverrides[] = [];
for (const key of Object.keys(raw)) {
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
const overrideRaw: any = {};
for (const keyInOverrideRaw in raw[key]) {
overrideRaw[keyInOverrideRaw] = raw[key][keyInOverrideRaw];
}
overrides.push({
identifiers: [overrideIdentifierFromKey(key).trim()],
keys: Object.keys(overrideRaw),
contents: toValuesTree(overrideRaw, conflictReporter)
});
}
}
return overrides;
}
export function toValuesTree(properties: { [qualifiedKey: string]: any }, conflictReporter: (message: string) => void): any {
const root = Object.create(null);
@@ -337,27 +278,6 @@ export function merge(base: any, add: any, overwrite: boolean): void {
});
}
export function getConfigurationKeys(): string[] {
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
return Object.keys(properties);
}
export function getDefaultValues(): any {
const valueTreeRoot: any = Object.create(null);
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
for (let key in properties) {
let value = properties[key].default;
addToValueTree(valueTreeRoot, key, value, message => console.error(`Conflict in default settings: ${message}`));
}
return valueTreeRoot;
}
export function keyFromOverrideIdentifier(overrideIdentifier: string): string {
return `[${overrideIdentifier}]`;
}
export function getMigratedSettingValue<T>(configurationService: IConfigurationService, currentSettingName: string, legacySettingName: string): T {
const setting = configurationService.inspect<T>(currentSettingName);
const legacySetting = configurationService.inspect<T>(legacySettingName);
@@ -370,3 +290,7 @@ export function getMigratedSettingValue<T>(configurationService: IConfigurationS
return setting.defaultValue!;
}
}
export function getLanguageTagSettingPlainKey(settingKey: string) {
return settingKey.replace(/[\[\]]/g, '');
}

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import * as arrays from 'vs/base/common/arrays';
import { IStringDictionary } from 'vs/base/common/collections';
import { Emitter, Event } from 'vs/base/common/event';
import * as json from 'vs/base/common/json';
import { Disposable } from 'vs/base/common/lifecycle';
@@ -12,9 +13,9 @@ import * as objects from 'vs/base/common/objects';
import { IExtUri } from 'vs/base/common/resources';
import * as types from 'vs/base/common/types';
import { URI, UriComponents } from 'vs/base/common/uri';
import { addToValueTree, compare, ConfigurationTarget, getConfigurationKeys, getConfigurationValue, getDefaultValues, IConfigurationChange, IConfigurationChangeEvent, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationValue, IOverrides, removeFromValueTree, toOverrides, toValuesTree } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope, Extensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifierFromKey, OVERRIDE_PROPERTY_PATTERN } from 'vs/platform/configuration/common/configurationRegistry';
import { IFileService } from 'vs/platform/files/common/files';
import { addToValueTree, ConfigurationTarget, getConfigurationValue, IConfigurationChange, IConfigurationChangeEvent, IConfigurationCompareResult, IConfigurationData, IConfigurationModel, IConfigurationOverrides, IConfigurationUpdateOverrides, IConfigurationValue, IOverrides, removeFromValueTree, toValuesTree } from 'vs/platform/configuration/common/configuration';
import { ConfigurationScope, Extensions, IConfigurationPropertySchema, IConfigurationRegistry, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from 'vs/platform/configuration/common/configurationRegistry';
import { FileOperation, IFileService } from 'vs/platform/files/common/files';
import { Registry } from 'vs/platform/registry/common/platform';
import { Workspace } from 'vs/platform/workspace/common/workspace';
@@ -58,12 +59,21 @@ export class ConfigurationModel implements IConfigurationModel {
}
getKeysForOverrideIdentifier(identifier: string): string[] {
const keys: string[] = [];
for (const override of this.overrides) {
if (override.identifiers.indexOf(identifier) !== -1) {
return override.keys;
if (override.identifiers.includes(identifier)) {
keys.push(...override.keys);
}
}
return [];
return arrays.distinct(keys);
}
getAllOverrideIdentifiers(): string[] {
const result: string[] = [];
for (const override of this.overrides) {
result.push(...override.identifiers);
}
return arrays.distinct(result);
}
override(identifier: string): ConfigurationModel {
@@ -87,6 +97,8 @@ export class ConfigurationModel implements IConfigurationModel {
const [override] = overrides.filter(o => arrays.equals(o.identifiers, otherOverride.identifiers));
if (override) {
this.mergeContents(override.contents, otherOverride.contents);
override.keys.push(...otherOverride.keys);
override.keys = arrays.distinct(override.keys);
} else {
overrides.push(objects.deepClone(otherOverride));
}
@@ -156,12 +168,27 @@ export class ConfigurationModel implements IConfigurationModel {
}
private getContentsForOverrideIdentifer(identifier: string): any {
let contentsForIdentifierOnly: IStringDictionary<any> | null = null;
let contents: IStringDictionary<any> | null = null;
const mergeContents = (contentsToMerge: any) => {
if (contentsToMerge) {
if (contents) {
this.mergeContents(contents, contentsToMerge);
} else {
contents = objects.deepClone(contentsToMerge);
}
}
};
for (const override of this.overrides) {
if (override.identifiers.indexOf(identifier) !== -1) {
return override.contents;
if (arrays.equals(override.identifiers, [identifier])) {
contentsForIdentifierOnly = override.contents;
} else if (override.identifiers.includes(identifier)) {
mergeContents(override.contents);
}
}
return null;
// Merge contents of the identifier only at the end to take precedence.
mergeContents(contentsForIdentifierOnly);
return contents;
}
toJSON(): IConfigurationModel {
@@ -207,19 +234,27 @@ export class ConfigurationModel implements IConfigurationModel {
export class DefaultConfigurationModel extends ConfigurationModel {
constructor() {
const contents = getDefaultValues();
const keys = getConfigurationKeys();
constructor(configurationDefaultsOverrides: IStringDictionary<any> = {}) {
const properties = Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties();
const keys = Object.keys(properties);
const contents: any = Object.create(null);
const overrides: IOverrides[] = [];
for (const key in properties) {
const defaultOverrideValue = configurationDefaultsOverrides[key];
const value = defaultOverrideValue !== undefined ? defaultOverrideValue : properties[key].default;
addToValueTree(contents, key, value, message => console.error(`Conflict in default settings: ${message}`));
}
for (const key of Object.keys(contents)) {
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
overrides.push({
identifiers: [overrideIdentifierFromKey(key).trim()],
identifiers: overrideIdentifiersFromKey(key),
keys: Object.keys(contents[key]),
contents: toValuesTree(contents[key], message => console.error(`Conflict in default settings file: ${message}`)),
});
}
}
super(contents, keys, overrides);
}
}
@@ -333,18 +368,18 @@ export class ConfigurationModelParser {
raw = filtered.raw;
const contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
const keys = Object.keys(raw);
const overrides: IOverrides[] = toOverrides(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
const overrides = this.toOverrides(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
return { contents, keys, overrides, restricted: filtered.restricted };
}
private filter(properties: any, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema | undefined }, filterOverriddenProperties: boolean, options?: ConfigurationParseOptions): { raw: {}, restricted: string[] } {
private filter(properties: any, configurationProperties: { [qualifiedKey: string]: IConfigurationPropertySchema | undefined }, filterOverriddenProperties: boolean, options?: ConfigurationParseOptions): { raw: {}; restricted: string[] } {
if (!options?.scopes && !options?.skipRestricted) {
return { raw: properties, restricted: [] };
}
const raw: any = {};
const restricted: string[] = [];
for (let key in properties) {
if (OVERRIDE_PROPERTY_PATTERN.test(key) && filterOverriddenProperties) {
if (OVERRIDE_PROPERTY_REGEX.test(key) && filterOverriddenProperties) {
const result = this.filter(properties[key], configurationProperties, false, options);
raw[key] = result.raw;
restricted.push(...result.restricted);
@@ -365,6 +400,24 @@ export class ConfigurationModelParser {
return { raw, restricted };
}
private toOverrides(raw: any, conflictReporter: (message: string) => void): IOverrides[] {
const overrides: IOverrides[] = [];
for (const key of Object.keys(raw)) {
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
const overrideRaw: any = {};
for (const keyInOverrideRaw in raw[key]) {
overrideRaw[keyInOverrideRaw] = raw[key][keyInOverrideRaw];
}
overrides.push({
identifiers: overrideIdentifiersFromKey(key),
keys: Object.keys(overrideRaw),
contents: toValuesTree(overrideRaw, conflictReporter)
});
}
}
return overrides;
}
}
export class UserSettings extends Disposable {
@@ -386,7 +439,10 @@ export class UserSettings extends Disposable {
this._register(this.fileService.watch(extUri.dirname(this.userSettingsResource)));
// Also listen to the resource incase the resource is a symlink - https://github.com/microsoft/vscode/issues/118134
this._register(this.fileService.watch(this.userSettingsResource));
this._register(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.userSettingsResource))(() => this._onDidChange.fire()));
this._register(Event.any(
Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.userSettingsResource)),
Event.filter(this.fileService.onDidRunOperation, e => (e.isOperation(FileOperation.CREATE) || e.isOperation(FileOperation.COPY) || e.isOperation(FileOperation.DELETE) || e.isOperation(FileOperation.WRITE)) && extUri.isEqual(e.resource, userSettingsResource))
)(() => this._onDidChange.fire()));
}
async loadConfiguration(): Promise<ConfigurationModel> {
@@ -431,7 +487,7 @@ export class Configuration {
return consolidateConfigurationModel.getValue(section);
}
updateValue(key: string, value: any, overrides: IConfigurationOverrides = {}): void {
updateValue(key: string, value: any, overrides: IConfigurationUpdateOverrides = {}): void {
let memoryConfiguration: ConfigurationModel | undefined;
if (overrides.resource) {
memoryConfiguration = this._memoryConfigurationByResource.get(overrides.resource);
@@ -542,11 +598,14 @@ export class Configuration {
this._foldersConsolidatedConfigurations.delete(resource);
}
compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel, keys: string[]): IConfigurationChange {
const overrides: [string, string[]][] = keys
.filter(key => OVERRIDE_PROPERTY_PATTERN.test(key))
.map(key => {
const overrideIdentifier = overrideIdentifierFromKey(key);
compareAndUpdateDefaultConfiguration(defaults: ConfigurationModel, keys?: string[]): IConfigurationChange {
const overrides: [string, string[]][] = [];
if (!keys) {
const { added, updated, removed } = compare(this._defaultConfiguration, defaults);
keys = [...added, ...updated, ...removed];
}
for (const key of keys) {
for (const overrideIdentifier of overrideIdentifiersFromKey(key)) {
const fromKeys = this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier);
const toKeys = defaults.getKeysForOverrideIdentifier(overrideIdentifier);
const keys = [
@@ -554,8 +613,9 @@ export class Configuration {
...fromKeys.filter(key => toKeys.indexOf(key) === -1),
...fromKeys.filter(key => !objects.equals(this._defaultConfiguration.override(overrideIdentifier).getValue(key), defaults.override(overrideIdentifier).getValue(key)))
];
return [overrideIdentifier, keys];
});
overrides.push([overrideIdentifier, keys]);
}
}
this.updateDefaultConfiguration(defaults);
return { keys, overrides };
}
@@ -732,6 +792,15 @@ export class Configuration {
return [...keys.values()];
}
protected allOverrideIdentifiers(): string[] {
const keys: Set<string> = new Set<string>();
this._defaultConfiguration.freeze().getAllOverrideIdentifiers().forEach(key => keys.add(key));
this.userConfiguration.freeze().getAllOverrideIdentifiers().forEach(key => keys.add(key));
this._workspaceConfiguration.freeze().getAllOverrideIdentifiers().forEach(key => keys.add(key));
this._folderConfigurations.forEach(folderConfiguraiton => folderConfiguraiton.freeze().getAllOverrideIdentifiers().forEach(key => keys.add(key)));
return [...keys.values()];
}
protected getAllKeysForOverrideIdentifier(overrideIdentifier: string): string[] {
const keys: Set<string> = new Set<string>();
this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
@@ -786,7 +855,7 @@ export class ConfigurationChangeEvent implements IConfigurationChangeEvent {
source!: ConfigurationTarget;
sourceConfig: any;
constructor(readonly change: IConfigurationChange, private readonly previous: { workspace?: Workspace, data: IConfigurationData } | undefined, private readonly currentConfiguraiton: Configuration, private readonly currentWorkspace?: Workspace) {
constructor(readonly change: IConfigurationChange, private readonly previous: { workspace?: Workspace; data: IConfigurationData } | undefined, private readonly currentConfiguraiton: Configuration, private readonly currentWorkspace?: Workspace) {
const keysSet = new Set<string>();
change.keys.forEach(key => keysSet.add(key));
change.overrides.forEach(([, keys]) => keys.forEach(key => keysSet.add(key)));
@@ -839,3 +908,59 @@ export class AllKeysConfigurationChangeEvent extends ConfigurationChangeEvent {
this.sourceConfig = sourceConfig;
}
}
function compare(from: ConfigurationModel | undefined, to: ConfigurationModel | undefined): IConfigurationCompareResult {
const { added, removed, updated } = compareConfigurationContents(to, from);
const overrides: [string, string[]][] = [];
const fromOverrideIdentifiers = from?.getAllOverrideIdentifiers() || [];
const toOverrideIdentifiers = to?.getAllOverrideIdentifiers() || [];
if (to) {
const addedOverrideIdentifiers = toOverrideIdentifiers.filter(key => !fromOverrideIdentifiers.includes(key));
for (const identifier of addedOverrideIdentifiers) {
overrides.push([identifier, to.getKeysForOverrideIdentifier(identifier)]);
}
}
if (from) {
const removedOverrideIdentifiers = fromOverrideIdentifiers.filter(key => !toOverrideIdentifiers.includes(key));
for (const identifier of removedOverrideIdentifiers) {
overrides.push([identifier, from.getKeysForOverrideIdentifier(identifier)]);
}
}
if (to && from) {
for (const identifier of fromOverrideIdentifiers) {
if (toOverrideIdentifiers.includes(identifier)) {
const result = compareConfigurationContents({ contents: from.getOverrideValue(undefined, identifier) || {}, keys: from.getKeysForOverrideIdentifier(identifier) }, { contents: to.getOverrideValue(undefined, identifier) || {}, keys: to.getKeysForOverrideIdentifier(identifier) });
overrides.push([identifier, [...result.added, ...result.removed, ...result.updated]]);
}
}
}
return { added, removed, updated, overrides };
}
function compareConfigurationContents(to: { keys: string[]; contents: any } | undefined, from: { keys: string[]; contents: any } | undefined) {
const added = to
? from ? to.keys.filter(key => from.keys.indexOf(key) === -1) : [...to.keys]
: [];
const removed = from
? to ? from.keys.filter(key => to.keys.indexOf(key) === -1) : [...from.keys]
: [];
const updated: string[] = [];
if (to && from) {
for (const key of from.keys) {
if (to.keys.indexOf(key) !== -1) {
const value1 = getConfigurationValue(from.contents, key);
const value2 = getConfigurationValue(to.contents, key);
if (!objects.equals(value1, value2)) {
updated.push(key);
}
}
}
}
return { added, removed, updated };
}

View File

@@ -9,6 +9,7 @@ import { Emitter, Event } from 'vs/base/common/event';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as types from 'vs/base/common/types';
import * as nls from 'vs/nls';
import { getLanguageTagSettingPlainKey } from 'vs/platform/configuration/common/configuration';
import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
@@ -43,17 +44,22 @@ export interface IConfigurationRegistry {
* - registering the configurations to add
* - dereigstering the configurations to remove
*/
updateConfigurations(configurations: { add: IConfigurationNode[], remove: IConfigurationNode[] }): void;
updateConfigurations(configurations: { add: IConfigurationNode[]; remove: IConfigurationNode[] }): void;
/**
* Register multiple default configurations to the registry.
*/
registerDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): void;
registerDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void;
/**
* Deregister multiple default configurations from the registry.
*/
deregisterDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): void;
deregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void;
/**
* Return the registered configuration defaults overrides
*/
getConfigurationDefaultsOverrides(): Map<string, IConfigurationDefaultOverride>;
/**
* Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values.
@@ -62,16 +68,16 @@ export interface IConfigurationRegistry {
notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]): void;
/**
* Event that fires whenver a configuration has been
* Event that fires whenever a configuration has been
* registered.
*/
onDidSchemaChange: Event<void>;
readonly onDidSchemaChange: Event<void>;
/**
* Event that fires whenver a configuration has been
* Event that fires whenever a configuration has been
* registered.
*/
onDidUpdateConfiguration: Event<string[]>;
readonly onDidUpdateConfiguration: Event<{ properties: string[]; defaultsOverrides?: boolean }>;
/**
* Returns all configuration nodes contributed to this registry.
@@ -81,12 +87,12 @@ export interface IConfigurationRegistry {
/**
* Returns all configurations settings of all configuration nodes contributed to this registry.
*/
getConfigurationProperties(): { [qualifiedKey: string]: IConfigurationPropertySchema };
getConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema>;
/**
* Returns all excluded configurations settings of all configuration nodes contributed to this registry.
*/
getExcludedConfigurationProperties(): { [qualifiedKey: string]: IConfigurationPropertySchema };
getExcludedConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema>;
/**
* Register the identifiers for editor configurations
@@ -131,8 +137,16 @@ export interface IConfigurationPropertySchema extends IJSONSchema {
*/
restricted?: boolean;
/**
* When `false` this property is excluded from the registry. Default is to include.
*/
included?: boolean;
/**
* List of tags associated to the property.
* - A tag can be used for filtering
* - Use `experimental` tag for marking the setting as experimental. **Note:** Defaults of experimental settings can be changed by the running experiments.
*/
tags?: string[];
/**
@@ -145,6 +159,9 @@ export interface IConfigurationPropertySchema extends IJSONSchema {
*/
disallowSyncIgnore?: boolean;
/**
* Labels for enumeration items
*/
enumItemLabels?: string[];
/**
@@ -152,11 +169,17 @@ export interface IConfigurationPropertySchema extends IJSONSchema {
* Otherwise, the presentation format defaults to `singleline`.
*/
editPresentation?: EditPresentationTypes;
/**
* When specified, gives an order number for the setting
* within the settings editor. Otherwise, the setting is placed at the end.
*/
order?: number;
}
export interface IConfigurationExtensionInfo {
export interface IExtensionInfo {
id: string;
restrictedConfigurations?: string[];
displayName?: string;
}
export interface IConfigurationNode {
@@ -165,41 +188,56 @@ export interface IConfigurationNode {
type?: string | string[];
title?: string;
description?: string;
properties?: { [path: string]: IConfigurationPropertySchema; };
properties?: IStringDictionary<IConfigurationPropertySchema>;
allOf?: IConfigurationNode[];
scope?: ConfigurationScope;
extensionInfo?: IConfigurationExtensionInfo;
extensionInfo?: IExtensionInfo;
restrictedProperties?: string[];
}
export const allSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const applicationSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const machineSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const machineOverridableSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const windowSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const resourceSettings: { properties: IStringDictionary<IConfigurationPropertySchema>, patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export interface IConfigurationDefaults {
overrides: IStringDictionary<any>;
source?: IExtensionInfo | string;
}
export type IRegisteredConfigurationPropertySchema = IConfigurationPropertySchema & {
defaultDefaultValue?: any;
source?: IExtensionInfo;
defaultValueSource?: IExtensionInfo | string;
};
export type IConfigurationDefaultOverride = { value: any; source?: IExtensionInfo | string };
export const allSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const applicationSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const machineSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const machineOverridableSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const windowSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const resourceSettings: { properties: IStringDictionary<IConfigurationPropertySchema>; patternProperties: IStringDictionary<IConfigurationPropertySchema> } = { properties: {}, patternProperties: {} };
export const resourceLanguageSettingsSchemaId = 'vscode://schemas/settings/resourceLanguage';
export const configurationDefaultsSchemaId = 'vscode://schemas/settings/configurationDefaults';
const contributionRegistry = Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
class ConfigurationRegistry implements IConfigurationRegistry {
private readonly defaultValues: IStringDictionary<any>;
private readonly configurationDefaultsOverrides: Map<string, IConfigurationDefaultOverride>;
private readonly defaultLanguageConfigurationOverridesNode: IConfigurationNode;
private readonly configurationContributors: IConfigurationNode[];
private readonly configurationProperties: { [qualifiedKey: string]: IJSONSchema };
private readonly excludedConfigurationProperties: { [qualifiedKey: string]: IJSONSchema };
private readonly configurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>;
private readonly excludedConfigurationProperties: IStringDictionary<IRegisteredConfigurationPropertySchema>;
private readonly resourceLanguageSettingsSchema: IJSONSchema;
private readonly overrideIdentifiers = new Set<string>();
private readonly _onDidSchemaChange = new Emitter<void>();
readonly onDidSchemaChange: Event<void> = this._onDidSchemaChange.event;
private readonly _onDidUpdateConfiguration: Emitter<string[]> = new Emitter<string[]>();
readonly onDidUpdateConfiguration: Event<string[]> = this._onDidUpdateConfiguration.event;
private readonly _onDidUpdateConfiguration = new Emitter<{ properties: string[]; defaultsOverrides?: boolean }>();
readonly onDidUpdateConfiguration = this._onDidUpdateConfiguration.event;
constructor() {
this.defaultValues = {};
this.configurationDefaultsOverrides = new Map<string, IConfigurationDefaultOverride>();
this.defaultLanguageConfigurationOverridesNode = {
id: 'defaultOverrides',
title: nls.localize('defaultLanguageConfigurationOverrides.title', "Default Language Configuration Overrides"),
@@ -211,6 +249,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.excludedConfigurationProperties = {};
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
this.registerOverridePropertyPatternKey();
}
public registerConfiguration(configuration: IConfigurationNode, validate: boolean = true): void {
@@ -222,7 +261,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
this._onDidSchemaChange.fire();
this._onDidUpdateConfiguration.fire(properties);
this._onDidUpdateConfiguration.fire({ properties });
}
public deregisterConfigurations(configurations: IConfigurationNode[]): void {
@@ -230,40 +269,44 @@ class ConfigurationRegistry implements IConfigurationRegistry {
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
this._onDidSchemaChange.fire();
this._onDidUpdateConfiguration.fire(properties);
this._onDidUpdateConfiguration.fire({ properties });
}
public updateConfigurations({ add, remove }: { add: IConfigurationNode[], remove: IConfigurationNode[] }): void {
public updateConfigurations({ add, remove }: { add: IConfigurationNode[]; remove: IConfigurationNode[] }): void {
const properties = [];
properties.push(...this.doDeregisterConfigurations(remove));
properties.push(...this.doRegisterConfigurations(add, false));
contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
this._onDidSchemaChange.fire();
this._onDidUpdateConfiguration.fire(distinct(properties));
this._onDidUpdateConfiguration.fire({ properties: distinct(properties) });
}
public registerDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): void {
public registerDefaultConfigurations(configurationDefaults: IConfigurationDefaults[]): void {
const properties: string[] = [];
const overrideIdentifiers: string[] = [];
for (const defaultConfiguration of defaultConfigurations) {
for (const key in defaultConfiguration) {
for (const { overrides, source } of configurationDefaults) {
for (const key in overrides) {
properties.push(key);
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
this.defaultValues[key] = { ...(this.defaultValues[key] || {}), ...defaultConfiguration[key] };
const property: IConfigurationPropertySchema = {
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
const defaultValue = { ...(this.configurationDefaultsOverrides.get(key)?.value || {}), ...overrides[key] };
this.configurationDefaultsOverrides.set(key, { source, value: defaultValue });
const plainKey = getLanguageTagSettingPlainKey(key);
const property: IRegisteredConfigurationPropertySchema = {
type: 'object',
default: this.defaultValues[key],
description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for {0} language.", key),
$ref: resourceLanguageSettingsSchemaId
default: defaultValue,
description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for the {0} language.", plainKey),
$ref: resourceLanguageSettingsSchemaId,
defaultDefaultValue: defaultValue,
source: types.isString(source) ? undefined : source,
};
overrideIdentifiers.push(overrideIdentifierFromKey(key));
overrideIdentifiers.push(...overrideIdentifiersFromKey(key));
this.configurationProperties[key] = property;
this.defaultLanguageConfigurationOverridesNode.properties![key] = property;
} else {
this.defaultValues[key] = defaultConfiguration[key];
this.configurationDefaultsOverrides.set(key, { value: overrides[key], source });
const property = this.configurationProperties[key];
if (property) {
this.updatePropertyDefaultValue(key, property);
@@ -275,16 +318,22 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.registerOverrideIdentifiers(overrideIdentifiers);
this._onDidSchemaChange.fire();
this._onDidUpdateConfiguration.fire(properties);
this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });
}
public deregisterDefaultConfigurations(defaultConfigurations: IStringDictionary<any>[]): void {
public deregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[]): void {
const properties: string[] = [];
for (const defaultConfiguration of defaultConfigurations) {
for (const key in defaultConfiguration) {
for (const { overrides, source } of defaultConfigurations) {
for (const key in overrides) {
const configurationDefaultsOverride = this.configurationDefaultsOverrides.get(key);
const id = types.isString(source) ? source : source?.id;
const configurationDefaultsOverrideSourceId = types.isString(configurationDefaultsOverride?.source) ? configurationDefaultsOverride?.source : configurationDefaultsOverride?.source?.id;
if (id !== configurationDefaultsOverrideSourceId) {
continue;
}
properties.push(key);
delete this.defaultValues[key];
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
this.configurationDefaultsOverrides.delete(key);
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
delete this.configurationProperties[key];
delete this.defaultLanguageConfigurationOverridesNode.properties![key];
} else {
@@ -299,7 +348,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this.updateOverridePropertyPatternKey();
this._onDidSchemaChange.fire();
this._onDidUpdateConfiguration.fire(properties);
this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });
}
public notifyConfigurationSchemaUpdated(...configurations: IConfigurationNode[]) {
@@ -316,7 +365,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
private doRegisterConfigurations(configurations: IConfigurationNode[], validate: boolean): string[] {
const properties: string[] = [];
configurations.forEach(configuration => {
properties.push(...this.validateAndRegisterProperties(configuration, validate, configuration.extensionInfo)); // fills in defaults
properties.push(...this.validateAndRegisterProperties(configuration, validate, configuration.extensionInfo, configuration.restrictedProperties)); // fills in defaults
this.configurationContributors.push(configuration);
this.registerJSONConfiguration(configuration);
});
@@ -347,7 +396,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
return properties;
}
private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, extensionInfo?: IConfigurationExtensionInfo, scope: ConfigurationScope = ConfigurationScope.WINDOW): string[] {
private validateAndRegisterProperties(configuration: IConfigurationNode, validate: boolean = true, extensionInfo: IExtensionInfo | undefined, restrictedProperties: string[] | undefined, scope: ConfigurationScope = ConfigurationScope.WINDOW): string[] {
scope = types.isUndefinedOrNull(configuration.scope) ? scope : configuration.scope;
let propertyKeys: string[] = [];
let properties = configuration.properties;
@@ -358,17 +407,19 @@ class ConfigurationRegistry implements IConfigurationRegistry {
continue;
}
const property = properties[key];
const property: IRegisteredConfigurationPropertySchema = properties[key];
property.source = extensionInfo;
// update default value
property.defaultDefaultValue = properties[key].default;
this.updatePropertyDefaultValue(key, property);
// update scope
if (OVERRIDE_PROPERTY_PATTERN.test(key)) {
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
property.scope = undefined; // No scope for overridable properties `[${identifier}]`
} else {
property.scope = types.isUndefinedOrNull(property.scope) ? scope : property.scope;
property.restricted = types.isUndefinedOrNull(property.restricted) ? !!extensionInfo?.restrictedConfigurations?.includes(key) : property.restricted;
property.restricted = types.isUndefinedOrNull(property.restricted) ? !!restrictedProperties?.includes(key) : property.restricted;
}
// Add to properties maps
@@ -392,24 +443,29 @@ class ConfigurationRegistry implements IConfigurationRegistry {
let subNodes = configuration.allOf;
if (subNodes) {
for (let node of subNodes) {
propertyKeys.push(...this.validateAndRegisterProperties(node, validate, extensionInfo, scope));
propertyKeys.push(...this.validateAndRegisterProperties(node, validate, extensionInfo, restrictedProperties, scope));
}
}
return propertyKeys;
}
// TODO: @sandy081 - Remove this method and include required info in getConfigurationProperties
getConfigurations(): IConfigurationNode[] {
return this.configurationContributors;
}
getConfigurationProperties(): { [qualifiedKey: string]: IConfigurationPropertySchema } {
getConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema> {
return this.configurationProperties;
}
getExcludedConfigurationProperties(): { [qualifiedKey: string]: IConfigurationPropertySchema } {
getExcludedConfigurationProperties(): IStringDictionary<IRegisteredConfigurationPropertySchema> {
return this.excludedConfigurationProperties;
}
getConfigurationDefaultsOverrides(): Map<string, IConfigurationDefaultOverride> {
return this.configurationDefaultsOverrides;
}
private registerJSONConfiguration(configuration: IConfigurationNode) {
const register = (configuration: IConfigurationNode) => {
let properties = configuration.properties;
@@ -469,6 +525,7 @@ class ConfigurationRegistry implements IConfigurationRegistry {
case ConfigurationScope.RESOURCE:
case ConfigurationScope.LANGUAGE_OVERRIDABLE:
delete resourceSettings.properties[key];
delete this.resourceLanguageSettingsSchema.properties![key];
break;
}
}
@@ -493,23 +550,60 @@ class ConfigurationRegistry implements IConfigurationRegistry {
this._onDidSchemaChange.fire();
}
private updatePropertyDefaultValue(key: string, property: IConfigurationPropertySchema): void {
let defaultValue = this.defaultValues[key];
private registerOverridePropertyPatternKey(): void {
const resourceLanguagePropertiesSchema: IJSONSchema = {
type: 'object',
description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."),
errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."),
$ref: resourceLanguageSettingsSchemaId,
};
allSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
applicationSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
machineSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
machineOverridableSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
windowSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
resourceSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
this._onDidSchemaChange.fire();
}
private updatePropertyDefaultValue(key: string, property: IRegisteredConfigurationPropertySchema): void {
const configurationdefaultOverride = this.configurationDefaultsOverrides.get(key);
let defaultValue = configurationdefaultOverride?.value;
let defaultSource = configurationdefaultOverride?.source;
if (types.isUndefined(defaultValue)) {
defaultValue = property.default;
defaultValue = property.defaultDefaultValue;
defaultSource = undefined;
}
if (types.isUndefined(defaultValue)) {
defaultValue = getDefaultValue(property.type);
}
property.default = defaultValue;
property.defaultValueSource = defaultSource;
}
}
const OVERRIDE_PROPERTY = '\\[.*\\]$';
export const OVERRIDE_PROPERTY_PATTERN = new RegExp(OVERRIDE_PROPERTY);
const OVERRIDE_IDENTIFIER_PATTERN = `\\[([^\\]]+)\\]`;
const OVERRIDE_IDENTIFIER_REGEX = new RegExp(OVERRIDE_IDENTIFIER_PATTERN, 'g');
export const OVERRIDE_PROPERTY_PATTERN = `^(${OVERRIDE_IDENTIFIER_PATTERN})+$`;
export const OVERRIDE_PROPERTY_REGEX = new RegExp(OVERRIDE_PROPERTY_PATTERN);
export function overrideIdentifierFromKey(key: string): string {
return key.substring(1, key.length - 1);
export function overrideIdentifiersFromKey(key: string): string[] {
const identifiers: string[] = [];
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
let matches = OVERRIDE_IDENTIFIER_REGEX.exec(key);
while (matches?.length) {
const identifier = matches[1].trim();
if (identifier) {
identifiers.push(identifier);
}
matches = OVERRIDE_IDENTIFIER_REGEX.exec(key);
}
}
return distinct(identifiers);
}
export function keyFromOverrideIdentifiers(overrideIdentifiers: string[]): string {
return overrideIdentifiers.reduce((result, overrideIdentifier) => `${result}[${overrideIdentifier}]`, '');
}
export function getDefaultValue(type: string | string[] | undefined): any {
@@ -531,7 +625,6 @@ export function getDefaultValue(type: string | string[] | undefined): any {
}
}
const configurationRegistry = new ConfigurationRegistry();
Registry.add(Extensions.Configuration, configurationRegistry);
@@ -539,7 +632,7 @@ export function validateProperty(property: string): string | null {
if (!property.trim()) {
return nls.localize('config.property.empty', "Cannot register an empty property");
}
if (OVERRIDE_PROPERTY_PATTERN.test(property)) {
if (OVERRIDE_PROPERTY_REGEX.test(property)) {
return nls.localize('config.property.languageDefault', "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", property);
}
if (configurationRegistry.getConfigurationProperties()[property] !== undefined) {

View File

@@ -34,7 +34,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel());
this.reloadConfigurationScheduler = this._register(new RunOnceScheduler(() => this.reloadConfiguration(), 50));
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(configurationProperties => this.onDidDefaultConfigurationChange(configurationProperties)));
this._register(Registry.as<IConfigurationRegistry>(Extensions.Configuration).onDidUpdateConfiguration(({ properties }) => this.onDidDefaultConfigurationChange(properties)));
this._register(this.userConfiguration.onDidChange(() => this.reloadConfigurationScheduler.schedule()));
}
@@ -89,9 +89,9 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
this.trigger(change, previous, ConfigurationTarget.USER);
}
private onDidDefaultConfigurationChange(keys: string[]): void {
private onDidDefaultConfigurationChange(properties: string[]): void {
const previous = this.configuration.toData();
const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel(), keys);
const change = this.configuration.compareAndUpdateDefaultConfiguration(new DefaultConfigurationModel(), properties);
this.trigger(change, previous, ConfigurationTarget.DEFAULT);
}

View File

@@ -1,99 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Queue } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
import { JSONPath, parse, ParseError } from 'vs/base/common/json';
import { setProperty } from 'vs/base/common/jsonEdit';
import { Edit, FormattingOptions } from 'vs/base/common/jsonFormatter';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { FileOperationError, FileOperationResult, IFileService, IWriteFileOptions } from 'vs/platform/files/common/files';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { ILogService } from 'vs/platform/log/common/log';
export const enum UserConfigurationErrorCode {
ERROR_INVALID_FILE = 'ERROR_INVALID_FILE',
ERROR_FILE_MODIFIED_SINCE = 'ERROR_FILE_MODIFIED_SINCE'
}
export interface IJSONValue {
path: JSONPath;
value: any;
}
export const UserConfigurationFileServiceId = 'IUserConfigurationFileService';
export const IUserConfigurationFileService = createDecorator<IUserConfigurationFileService>(UserConfigurationFileServiceId);
export interface IUserConfigurationFileService {
readonly _serviceBrand: undefined;
updateSettings(value: IJSONValue, formattingOptions: FormattingOptions): Promise<void>;
write(value: VSBuffer, options?: IWriteFileOptions): Promise<void>;
}
export class UserConfigurationFileService implements IUserConfigurationFileService {
readonly _serviceBrand: undefined;
private readonly queue: Queue<void>;
constructor(
@IEnvironmentService private readonly environmentService: IEnvironmentService,
@IFileService private readonly fileService: IFileService,
@ILogService private readonly logService: ILogService,
) {
this.queue = new Queue<void>();
}
async updateSettings(value: IJSONValue, formattingOptions: FormattingOptions): Promise<void> {
return this.queue.queue(() => this.doWrite(value, formattingOptions)); // queue up writes to prevent race conditions
}
private async doWrite(jsonValue: IJSONValue, formattingOptions: FormattingOptions): Promise<void> {
this.logService.trace(`${UserConfigurationFileServiceId}#write`, this.environmentService.settingsResource.toString(), jsonValue);
const { value, mtime, etag } = await this.fileService.readFile(this.environmentService.settingsResource, { atomic: true });
let content = value.toString();
const parseErrors: ParseError[] = [];
parse(content, parseErrors, { allowTrailingComma: true, allowEmptyContent: true });
if (parseErrors.length) {
throw new Error(UserConfigurationErrorCode.ERROR_INVALID_FILE);
}
const edit = this.getEdits(jsonValue, content, formattingOptions)[0];
if (edit) {
content = content.substring(0, edit.offset) + edit.content + content.substring(edit.offset + edit.length);
try {
await this.fileService.writeFile(this.environmentService.settingsResource, VSBuffer.fromString(content), { etag, mtime });
} catch (error) {
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_MODIFIED_SINCE) {
throw new Error(UserConfigurationErrorCode.ERROR_FILE_MODIFIED_SINCE);
}
}
}
}
async write(content: VSBuffer, options?: IWriteFileOptions): Promise<void> {
// queue up writes to prevent race conditions
return this.queue.queue(async () => {
await this.fileService.writeFile(this.environmentService.settingsResource, content, options);
});
}
private getEdits({ value, path }: IJSONValue, modelContent: string, formattingOptions: FormattingOptions): Edit[] {
if (path.length) {
return setProperty(modelContent, path, value, formattingOptions);
}
// Without jsonPath, the entire configuration file is being replaced, so we just use JSON.stringify
const content = JSON.stringify(value, null, formattingOptions.insertSpaces && formattingOptions.tabSize ? ' '.repeat(formattingOptions.tabSize) : '\t');
return [{
content,
length: modelContent.length,
offset: 0
}];
}
}

View File

@@ -17,6 +17,18 @@ suite('Configuration', () => {
assert.deepStrictEqual(base, { 'a': 1, 'b': 2, 'c': 4 });
});
test('object merge', () => {
let base = { 'a': { 'b': 1, 'c': true, 'd': 2 } };
merge(base, { 'a': { 'b': undefined, 'c': false, 'e': 'a' } }, true);
assert.deepStrictEqual(base, { 'a': { 'b': undefined, 'c': false, 'd': 2, 'e': 'a' } });
});
test('array merge', () => {
let base = { 'a': ['b', 'c'] };
merge(base, { 'a': ['b', 'd'] }, true);
assert.deepStrictEqual(base, { 'a': ['b', 'd'] });
});
test('removeFromValueTree: remove a non existing key', () => {
let target = { 'a': { 'b': 2 } };

View File

@@ -12,6 +12,35 @@ import { Registry } from 'vs/platform/registry/common/platform';
import { WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { Workspace } from 'vs/platform/workspace/test/common/testWorkspace';
suite('ConfigurationModelParser', () => {
test('parse configuration model with single override identifier', () => {
const testObject = new ConfigurationModelParser('');
testObject.parse(JSON.stringify({ '[x]': { 'a': 1 } }));
assert.deepStrictEqual(JSON.stringify(testObject.configurationModel.overrides), JSON.stringify([{ identifiers: ['x'], keys: ['a'], contents: { 'a': 1 } }]));
});
test('parse configuration model with multiple override identifiers', () => {
const testObject = new ConfigurationModelParser('');
testObject.parse(JSON.stringify({ '[x][y]': { 'a': 1 } }));
assert.deepStrictEqual(JSON.stringify(testObject.configurationModel.overrides), JSON.stringify([{ identifiers: ['x', 'y'], keys: ['a'], contents: { 'a': 1 } }]));
});
test('parse configuration model with multiple duplicate override identifiers', () => {
const testObject = new ConfigurationModelParser('');
testObject.parse(JSON.stringify({ '[x][y][x][z]': { 'a': 1 } }));
assert.deepStrictEqual(JSON.stringify(testObject.configurationModel.overrides), JSON.stringify([{ identifiers: ['x', 'y', 'z'], keys: ['a'], contents: { 'a': 1 } }]));
});
});
suite('ConfigurationModel', () => {
test('setValue for a key that has no sections and not defined', () => {
@@ -190,7 +219,7 @@ suite('ConfigurationModel', () => {
let result = base.merge(add);
assert.deepStrictEqual(result.contents, { 'a': { 'b': 2 } });
assert.deepStrictEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 }, keys: ['a'] }]);
assert.deepStrictEqual(result.overrides, [{ identifiers: ['c'], contents: { 'a': 2, 'b': 2 }, keys: ['a', 'b'] }]);
assert.deepStrictEqual(result.override('c').contents, { 'a': 2, 'b': 2 });
assert.deepStrictEqual(result.keys, ['a.b']);
});
@@ -236,6 +265,45 @@ suite('ConfigurationModel', () => {
assert.deepStrictEqual(testObject.override('b').contents, { 'a': 2, 'c': 1 });
});
test('Test override when an override has multiple identifiers', () => {
const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, ['a', 'c'], [{ identifiers: ['x', 'y'], contents: { 'a': 2 }, keys: ['a'] }]);
let actual = testObject.override('x');
assert.deepStrictEqual(actual.contents, { 'a': 2, 'c': 1 });
assert.deepStrictEqual(actual.keys, ['a', 'c']);
assert.deepStrictEqual(testObject.getKeysForOverrideIdentifier('x'), ['a']);
actual = testObject.override('y');
assert.deepStrictEqual(actual.contents, { 'a': 2, 'c': 1 });
assert.deepStrictEqual(actual.keys, ['a', 'c']);
assert.deepStrictEqual(testObject.getKeysForOverrideIdentifier('y'), ['a']);
});
test('Test override when an identifier is defined in multiple overrides', () => {
const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, ['a', 'c'], [{ identifiers: ['x'], contents: { 'a': 3, 'b': 1 }, keys: ['a', 'b'] }, { identifiers: ['x', 'y'], contents: { 'a': 2 }, keys: ['a'] }]);
const actual = testObject.override('x');
assert.deepStrictEqual(actual.contents, { 'a': 3, 'c': 1, 'b': 1 });
assert.deepStrictEqual(actual.keys, ['a', 'c']);
assert.deepStrictEqual(testObject.getKeysForOverrideIdentifier('x'), ['a', 'b']);
});
test('Test merge when configuration models have multiple identifiers', () => {
const testObject = new ConfigurationModel({ 'a': 1, 'c': 1 }, ['a', 'c'], [{ identifiers: ['y'], contents: { 'c': 1 }, keys: ['c'] }, { identifiers: ['x', 'y'], contents: { 'a': 2 }, keys: ['a'] }]);
const target = new ConfigurationModel({ 'a': 2, 'b': 1 }, ['a', 'b'], [{ identifiers: ['x'], contents: { 'a': 3, 'b': 2 }, keys: ['a', 'b'] }, { identifiers: ['x', 'y'], contents: { 'b': 3 }, keys: ['b'] }]);
const actual = testObject.merge(target);
assert.deepStrictEqual(actual.contents, { 'a': 2, 'c': 1, 'b': 1 });
assert.deepStrictEqual(actual.keys, ['a', 'c', 'b']);
assert.deepStrictEqual(actual.overrides, [
{ identifiers: ['y'], contents: { 'c': 1 }, keys: ['c'] },
{ identifiers: ['x', 'y'], contents: { 'a': 2, 'b': 3 }, keys: ['a', 'b'] },
{ identifiers: ['x'], contents: { 'a': 3, 'b': 2 }, keys: ['a', 'b'] },
]);
});
});
suite('CustomConfigurationModel', () => {
@@ -375,6 +443,43 @@ suite('CustomConfigurationModel', () => {
});
});
suite('CustomConfigurationModel', () => {
test('Default configuration model uses overrides', () => {
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
'id': 'a',
'order': 1,
'title': 'a',
'type': 'object',
'properties': {
'a': {
'description': 'a',
'type': 'boolean',
'default': false,
}
}
});
assert.strictEqual(true, new DefaultConfigurationModel().getValue('a'));
});
test('Default configuration model uses overrides', () => {
Registry.as<IConfigurationRegistry>(Extensions.Configuration).registerConfiguration({
'id': 'a',
'order': 1,
'title': 'a',
'type': 'object',
'properties': {
'a': {
'description': 'a',
'type': 'boolean',
'default': false,
}
}
});
assert.strictEqual(false, new DefaultConfigurationModel({ a: false }).getValue('a'));
});
});
suite('Configuration', () => {
test('Test inspect for overrideIdentifiers', () => {
@@ -582,11 +687,14 @@ suite('ConfigurationChangeEvent', () => {
'files.autoSave': 'off',
'[markdown]': {
'editor.wordWrap': 'off'
},
'[typescript][jsonc]': {
'editor.lineNumbers': 'off'
}
}));
let testObject = new ConfigurationChangeEvent(change, undefined, configuration);
assert.deepStrictEqual(testObject.affectedKeys, ['files.autoSave', '[markdown]', 'editor.wordWrap']);
assert.deepStrictEqual(testObject.affectedKeys, ['files.autoSave', '[markdown]', '[typescript][jsonc]', 'editor.wordWrap', 'editor.lineNumbers']);
assert.ok(testObject.affectsConfiguration('files'));
assert.ok(testObject.affectsConfiguration('files.autoSave'));
@@ -598,8 +706,16 @@ suite('ConfigurationChangeEvent', () => {
assert.ok(testObject.affectsConfiguration('editor'));
assert.ok(testObject.affectsConfiguration('editor.wordWrap'));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers'));
assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'jsonc' }));
assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'typescript' }));
assert.ok(testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'jsonc' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'typescript' }));
assert.ok(!testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'typescript' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'jsonc' }));
assert.ok(!testObject.affectsConfiguration('editor', { overrideIdentifier: 'json' }));
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'markdown' }));
@@ -615,6 +731,10 @@ suite('ConfigurationChangeEvent', () => {
'editor.fontSize': 12,
'editor.wordWrap': 'off'
},
'[css][scss]': {
'editor.lineNumbers': 'off',
'css.lint.emptyRules': 'error'
},
'files.autoSave': 'off',
}));
const data = configuration.toData();
@@ -624,11 +744,15 @@ suite('ConfigurationChangeEvent', () => {
'editor.fontSize': 13,
'editor.wordWrap': 'off'
},
'[css][scss]': {
'editor.lineNumbers': 'relative',
'css.lint.emptyRules': 'error'
},
'window.zoomLevel': 1,
}));
let testObject = new ConfigurationChangeEvent(change, { data }, configuration);
assert.deepStrictEqual(testObject.affectedKeys, ['window.zoomLevel', '[markdown]', 'workbench.editor.enablePreview', 'editor.fontSize']);
assert.deepStrictEqual(testObject.affectedKeys, ['window.zoomLevel', '[markdown]', '[css][scss]', 'workbench.editor.enablePreview', 'editor.fontSize', 'editor.lineNumbers']);
assert.ok(!testObject.affectsConfiguration('files'));
@@ -637,10 +761,18 @@ suite('ConfigurationChangeEvent', () => {
assert.ok(!testObject.affectsConfiguration('[markdown].editor.fontSize'));
assert.ok(!testObject.affectsConfiguration('[markdown].editor.wordWrap'));
assert.ok(!testObject.affectsConfiguration('[markdown].workbench'));
assert.ok(testObject.affectsConfiguration('[css][scss]'));
assert.ok(testObject.affectsConfiguration('editor'));
assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'markdown' }));
assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'css' }));
assert.ok(testObject.affectsConfiguration('editor', { overrideIdentifier: 'scss' }));
assert.ok(testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'css' }));
assert.ok(!testObject.affectsConfiguration('editor.fontSize', { overrideIdentifier: 'scss' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'scss' }));
assert.ok(testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'css' }));
assert.ok(!testObject.affectsConfiguration('editor.lineNumbers', { overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap'));
assert.ok(!testObject.affectsConfiguration('editor.wordWrap', { overrideIdentifier: 'markdown' }));
assert.ok(!testObject.affectsConfiguration('editor', { overrideIdentifier: 'json' }));

View File

@@ -21,16 +21,16 @@ suite('ConfigurationRegistry', () => {
}
}
});
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 1, b: 2 } }]);
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 2, c: 3 } }]);
configurationRegistry.registerDefaultConfigurations([{ overrides: { 'config': { a: 1, b: 2 } } }]);
configurationRegistry.registerDefaultConfigurations([{ overrides: { '[lang]': { a: 2, c: 3 } } }]);
assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 1, b: 2 });
assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, c: 3 });
});
test('configuration override defaults - merges defaults', async () => {
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 1, b: 2 } }]);
configurationRegistry.registerDefaultConfigurations([{ '[lang]': { a: 2, c: 3 } }]);
configurationRegistry.registerDefaultConfigurations([{ overrides: { '[lang]': { a: 1, b: 2 } } }]);
configurationRegistry.registerDefaultConfigurations([{ overrides: { '[lang]': { a: 2, c: 3 } } }]);
assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, b: 2, c: 3 });
});
@@ -45,8 +45,8 @@ suite('ConfigurationRegistry', () => {
}
}
});
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 1, b: 2 } }]);
configurationRegistry.registerDefaultConfigurations([{ 'config': { a: 2, c: 3 } }]);
configurationRegistry.registerDefaultConfigurations([{ overrides: { 'config': { a: 1, b: 2 } } }]);
configurationRegistry.registerDefaultConfigurations([{ overrides: { 'config': { a: 2, c: 3 } } }]);
assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 2, c: 3 });
});

View File

@@ -139,7 +139,7 @@ suite('ConfigurationService', () => {
configuration: {
service: {
testSetting: string;
}
};
};
}

View File

@@ -6,13 +6,16 @@
import { Emitter } from 'vs/base/common/event';
import { TernarySearchTree } from 'vs/base/common/map';
import { URI } from 'vs/base/common/uri';
import { getConfigurationKeys, getConfigurationValue, IConfigurationOverrides, IConfigurationService, IConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
import { getConfigurationValue, IConfigurationChangeEvent, IConfigurationOverrides, IConfigurationService, IConfigurationValue, isConfigurationOverrides } from 'vs/platform/configuration/common/configuration';
import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry';
import { Registry } from 'vs/platform/registry/common/platform';
export class TestConfigurationService implements IConfigurationService {
public _serviceBrand: undefined;
private configuration: any;
readonly onDidChangeConfiguration = new Emitter<any>().event;
readonly onDidChangeConfigurationEmitter = new Emitter<IConfigurationChangeEvent>();
readonly onDidChangeConfiguration = this.onDidChangeConfigurationEmitter.event;
constructor(configuration?: any) {
this.configuration = configuration || Object.create(null);
@@ -55,19 +58,25 @@ export class TestConfigurationService implements IConfigurationService {
return Promise.resolve(undefined);
}
private overrideIdentifiers: Map<string, string[]> = new Map();
public setOverrideIdentifiers(key: string, identifiers: string[]): void {
this.overrideIdentifiers.set(key, identifiers);
}
public inspect<T>(key: string, overrides?: IConfigurationOverrides): IConfigurationValue<T> {
const config = this.getValue(undefined, overrides);
return {
value: getConfigurationValue<T>(config, key),
defaultValue: getConfigurationValue<T>(config, key),
userValue: getConfigurationValue<T>(config, key)
userValue: getConfigurationValue<T>(config, key),
overrideIdentifiers: this.overrideIdentifiers.get(key)
};
}
public keys() {
return {
default: getConfigurationKeys(),
default: Object.keys(Registry.as<IConfigurationRegistry>(Extensions.Configuration).getConfigurationProperties()),
user: Object.keys(this.configuration),
workspace: [],
workspaceFolder: []