sussudio
Version:
An unofficial VS Code Internal API
1,052 lines (1,051 loc) • 48 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as arrays from "../../../base/common/arrays.mjs";
import { Emitter, Event } from "../../../base/common/event.mjs";
import * as json from "../../../base/common/json.mjs";
import { Disposable } from "../../../base/common/lifecycle.mjs";
import { getOrSet, ResourceMap } from "../../../base/common/map.mjs";
import * as objects from "../../../base/common/objects.mjs";
import * as types from "../../../base/common/types.mjs";
import { URI } from "../../../base/common/uri.mjs";
import { addToValueTree, getConfigurationValue, removeFromValueTree, toValuesTree } from "./configuration.mjs";
import { Extensions, overrideIdentifiersFromKey, OVERRIDE_PROPERTY_REGEX } from "./configurationRegistry.mjs";
import { Registry } from "../../registry/common/platform.mjs";
export class ConfigurationModel {
_contents;
_keys;
_overrides;
raw;
frozen = false;
overrideConfigurations = new Map();
constructor(_contents = {}, _keys = [], _overrides = [], raw) {
this._contents = _contents;
this._keys = _keys;
this._overrides = _overrides;
this.raw = raw;
}
_rawConfiguration;
get rawConfiguration() {
if (!this._rawConfiguration) {
if (this.raw?.length) {
const rawConfigurationModels = this.raw.map(raw => {
if (raw instanceof ConfigurationModel) {
return raw;
}
const parser = new ConfigurationModelParser('');
parser.parseRaw(raw);
return parser.configurationModel;
});
this._rawConfiguration = rawConfigurationModels.reduce((previous, current) => current === previous ? current : previous.merge(current), rawConfigurationModels[0]);
}
else {
// raw is same as current
this._rawConfiguration = this;
}
}
return this._rawConfiguration;
}
get contents() {
return this.checkAndFreeze(this._contents);
}
get overrides() {
return this.checkAndFreeze(this._overrides);
}
get keys() {
return this.checkAndFreeze(this._keys);
}
isEmpty() {
return this._keys.length === 0 && Object.keys(this._contents).length === 0 && this._overrides.length === 0;
}
isFrozen() {
return this.frozen;
}
getValue(section) {
return section ? getConfigurationValue(this.contents, section) : this.contents;
}
inspect(section, overrideIdentifier) {
const value = this.rawConfiguration.getValue(section);
const override = overrideIdentifier ? this.rawConfiguration.getOverrideValue(section, overrideIdentifier) : undefined;
const merged = overrideIdentifier ? this.rawConfiguration.override(overrideIdentifier).getValue(section) : value;
return { value, override, merged };
}
getOverrideValue(section, overrideIdentifier) {
const overrideContents = this.getContentsForOverrideIdentifer(overrideIdentifier);
return overrideContents
? section ? getConfigurationValue(overrideContents, section) : overrideContents
: undefined;
}
getKeysForOverrideIdentifier(identifier) {
const keys = [];
for (const override of this.overrides) {
if (override.identifiers.includes(identifier)) {
keys.push(...override.keys);
}
}
return arrays.distinct(keys);
}
getAllOverrideIdentifiers() {
const result = [];
for (const override of this.overrides) {
result.push(...override.identifiers);
}
return arrays.distinct(result);
}
override(identifier) {
let overrideConfigurationModel = this.overrideConfigurations.get(identifier);
if (!overrideConfigurationModel) {
overrideConfigurationModel = this.createOverrideConfigurationModel(identifier);
this.overrideConfigurations.set(identifier, overrideConfigurationModel);
}
return overrideConfigurationModel;
}
merge(...others) {
const contents = objects.deepClone(this.contents);
const overrides = objects.deepClone(this.overrides);
const keys = [...this.keys];
const raws = this.raw?.length ? [...this.raw] : [this];
for (const other of others) {
raws.push(...(other.raw?.length ? other.raw : [other]));
if (other.isEmpty()) {
continue;
}
this.mergeContents(contents, other.contents);
for (const otherOverride of other.overrides) {
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));
}
}
for (const key of other.keys) {
if (keys.indexOf(key) === -1) {
keys.push(key);
}
}
}
return new ConfigurationModel(contents, keys, overrides, raws.every(raw => raw instanceof ConfigurationModel) ? undefined : raws);
}
freeze() {
this.frozen = true;
return this;
}
createOverrideConfigurationModel(identifier) {
const overrideContents = this.getContentsForOverrideIdentifer(identifier);
if (!overrideContents || typeof overrideContents !== 'object' || !Object.keys(overrideContents).length) {
// If there are no valid overrides, return self
return this;
}
const contents = {};
for (const key of arrays.distinct([...Object.keys(this.contents), ...Object.keys(overrideContents)])) {
let contentsForKey = this.contents[key];
const overrideContentsForKey = overrideContents[key];
// If there are override contents for the key, clone and merge otherwise use base contents
if (overrideContentsForKey) {
// Clone and merge only if base contents and override contents are of type object otherwise just override
if (typeof contentsForKey === 'object' && typeof overrideContentsForKey === 'object') {
contentsForKey = objects.deepClone(contentsForKey);
this.mergeContents(contentsForKey, overrideContentsForKey);
}
else {
contentsForKey = overrideContentsForKey;
}
}
contents[key] = contentsForKey;
}
return new ConfigurationModel(contents, this.keys, this.overrides);
}
mergeContents(source, target) {
for (const key of Object.keys(target)) {
if (key in source) {
if (types.isObject(source[key]) && types.isObject(target[key])) {
this.mergeContents(source[key], target[key]);
continue;
}
}
source[key] = objects.deepClone(target[key]);
}
}
checkAndFreeze(data) {
if (this.frozen && !Object.isFrozen(data)) {
return objects.deepFreeze(data);
}
return data;
}
getContentsForOverrideIdentifer(identifier) {
let contentsForIdentifierOnly = null;
let contents = null;
const mergeContents = (contentsToMerge) => {
if (contentsToMerge) {
if (contents) {
this.mergeContents(contents, contentsToMerge);
}
else {
contents = objects.deepClone(contentsToMerge);
}
}
};
for (const override of this.overrides) {
if (arrays.equals(override.identifiers, [identifier])) {
contentsForIdentifierOnly = override.contents;
}
else if (override.identifiers.includes(identifier)) {
mergeContents(override.contents);
}
}
// Merge contents of the identifier only at the end to take precedence.
mergeContents(contentsForIdentifierOnly);
return contents;
}
toJSON() {
return {
contents: this.contents,
overrides: this.overrides,
keys: this.keys
};
}
// Update methods
setValue(key, value) {
this.addKey(key);
addToValueTree(this.contents, key, value, e => { throw new Error(e); });
}
removeValue(key) {
if (this.removeKey(key)) {
removeFromValueTree(this.contents, key);
}
}
addKey(key) {
let index = this.keys.length;
for (let i = 0; i < index; i++) {
if (key.indexOf(this.keys[i]) === 0) {
index = i;
}
}
this.keys.splice(index, 1, key);
}
removeKey(key) {
const index = this.keys.indexOf(key);
if (index !== -1) {
this.keys.splice(index, 1);
return true;
}
return false;
}
}
export class ConfigurationModelParser {
_name;
_raw = null;
_configurationModel = null;
_restrictedConfigurations = [];
_parseErrors = [];
constructor(_name) {
this._name = _name;
}
get configurationModel() {
return this._configurationModel || new ConfigurationModel();
}
get restrictedConfigurations() {
return this._restrictedConfigurations;
}
get errors() {
return this._parseErrors;
}
parse(content, options) {
if (!types.isUndefinedOrNull(content)) {
const raw = this.doParseContent(content);
this.parseRaw(raw, options);
}
}
reparse(options) {
if (this._raw) {
this.parseRaw(this._raw, options);
}
}
parseRaw(raw, options) {
this._raw = raw;
const { contents, keys, overrides, restricted, hasExcludedProperties } = this.doParseRaw(raw, options);
this._configurationModel = new ConfigurationModel(contents, keys, overrides, hasExcludedProperties ? [raw] : undefined /* raw has not changed */);
this._restrictedConfigurations = restricted || [];
}
doParseContent(content) {
let raw = {};
let currentProperty = null;
let currentParent = [];
const previousParents = [];
const parseErrors = [];
function onValue(value) {
if (Array.isArray(currentParent)) {
currentParent.push(value);
}
else if (currentProperty !== null) {
currentParent[currentProperty] = value;
}
}
const visitor = {
onObjectBegin: () => {
const object = {};
onValue(object);
previousParents.push(currentParent);
currentParent = object;
currentProperty = null;
},
onObjectProperty: (name) => {
currentProperty = name;
},
onObjectEnd: () => {
currentParent = previousParents.pop();
},
onArrayBegin: () => {
const array = [];
onValue(array);
previousParents.push(currentParent);
currentParent = array;
currentProperty = null;
},
onArrayEnd: () => {
currentParent = previousParents.pop();
},
onLiteralValue: onValue,
onError: (error, offset, length) => {
parseErrors.push({ error, offset, length });
}
};
if (content) {
try {
json.visit(content, visitor);
raw = currentParent[0] || {};
}
catch (e) {
console.error(`Error while parsing settings file ${this._name}: ${e}`);
this._parseErrors = [e];
}
}
return raw;
}
doParseRaw(raw, options) {
const configurationProperties = Registry.as(Extensions.Configuration).getConfigurationProperties();
const filtered = this.filter(raw, configurationProperties, true, options);
raw = filtered.raw;
const contents = toValuesTree(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
const keys = Object.keys(raw);
const overrides = this.toOverrides(raw, message => console.error(`Conflict in settings file ${this._name}: ${message}`));
return { contents, keys, overrides, restricted: filtered.restricted, hasExcludedProperties: filtered.hasExcludedProperties };
}
filter(properties, configurationProperties, filterOverriddenProperties, options) {
let hasExcludedProperties = false;
if (!options?.scopes && !options?.skipRestricted) {
return { raw: properties, restricted: [], hasExcludedProperties };
}
const raw = {};
const restricted = [];
for (const key in properties) {
if (OVERRIDE_PROPERTY_REGEX.test(key) && filterOverriddenProperties) {
const result = this.filter(properties[key], configurationProperties, false, options);
raw[key] = result.raw;
hasExcludedProperties = hasExcludedProperties || result.hasExcludedProperties;
restricted.push(...result.restricted);
}
else {
const propertySchema = configurationProperties[key];
const scope = propertySchema ? typeof propertySchema.scope !== 'undefined' ? propertySchema.scope : 3 /* ConfigurationScope.WINDOW */ : undefined;
if (propertySchema?.restricted) {
restricted.push(key);
}
// Load unregistered configurations always.
if ((scope === undefined || options.scopes === undefined || options.scopes.includes(scope)) // Check scopes
&& !(options.skipRestricted && propertySchema?.restricted)) { // Check restricted
raw[key] = properties[key];
}
else {
hasExcludedProperties = true;
}
}
}
return { raw, restricted, hasExcludedProperties };
}
toOverrides(raw, conflictReporter) {
const overrides = [];
for (const key of Object.keys(raw)) {
if (OVERRIDE_PROPERTY_REGEX.test(key)) {
const overrideRaw = {};
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 {
userSettingsResource;
scopes;
fileService;
parser;
parseOptions;
_onDidChange = this._register(new Emitter());
onDidChange = this._onDidChange.event;
constructor(userSettingsResource, scopes, extUri, fileService) {
super();
this.userSettingsResource = userSettingsResource;
this.scopes = scopes;
this.fileService = fileService;
this.parser = new ConfigurationModelParser(this.userSettingsResource.toString());
this.parseOptions = { scopes: this.scopes };
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.any(Event.filter(this.fileService.onDidFilesChange, e => e.contains(this.userSettingsResource)), Event.filter(this.fileService.onDidRunOperation, e => (e.isOperation(0 /* FileOperation.CREATE */) || e.isOperation(3 /* FileOperation.COPY */) || e.isOperation(1 /* FileOperation.DELETE */) || e.isOperation(4 /* FileOperation.WRITE */)) && extUri.isEqual(e.resource, userSettingsResource)))(() => this._onDidChange.fire()));
}
async loadConfiguration() {
try {
const content = await this.fileService.readFile(this.userSettingsResource);
this.parser.parse(content.value.toString() || '{}', this.parseOptions);
return this.parser.configurationModel;
}
catch (e) {
return new ConfigurationModel();
}
}
reparse() {
this.parser.reparse(this.parseOptions);
return this.parser.configurationModel;
}
getRestrictedSettings() {
return this.parser.restrictedConfigurations;
}
}
class ConfigurationInspectValue {
key;
overrides;
value;
overrideIdentifiers;
defaultConfiguration;
policyConfiguration;
applicationConfiguration;
userConfiguration;
localUserConfiguration;
remoteUserConfiguration;
workspaceConfiguration;
folderConfigurationModel;
memoryInspectValue;
constructor(key, overrides, value, overrideIdentifiers, defaultConfiguration, policyConfiguration, applicationConfiguration, userConfiguration, localUserConfiguration, remoteUserConfiguration, workspaceConfiguration, folderConfigurationModel, memoryInspectValue) {
this.key = key;
this.overrides = overrides;
this.value = value;
this.overrideIdentifiers = overrideIdentifiers;
this.defaultConfiguration = defaultConfiguration;
this.policyConfiguration = policyConfiguration;
this.applicationConfiguration = applicationConfiguration;
this.userConfiguration = userConfiguration;
this.localUserConfiguration = localUserConfiguration;
this.remoteUserConfiguration = remoteUserConfiguration;
this.workspaceConfiguration = workspaceConfiguration;
this.folderConfigurationModel = folderConfigurationModel;
this.memoryInspectValue = memoryInspectValue;
}
_defaultInspectValue;
get defaultInspectValue() {
if (!this._defaultInspectValue) {
this._defaultInspectValue = this.defaultConfiguration.inspect(this.key, this.overrides.overrideIdentifier);
}
return this._defaultInspectValue;
}
get defaultValue() {
return this.defaultInspectValue.merged;
}
get default() {
return this.defaultInspectValue.value !== undefined || this.defaultInspectValue.override !== undefined ? { value: this.defaultInspectValue.value, override: this.defaultInspectValue.override } : undefined;
}
_policyInspectValue;
get policyInspectValue() {
if (this._policyInspectValue === undefined) {
this._policyInspectValue = this.policyConfiguration ? this.policyConfiguration.inspect(this.key) : null;
}
return this._policyInspectValue;
}
get policyValue() {
return this.policyInspectValue?.merged;
}
get policy() {
return this.policyInspectValue?.value !== undefined ? { value: this.policyInspectValue.value } : undefined;
}
_applicationInspectValue;
get applicationInspectValue() {
if (this._applicationInspectValue === undefined) {
this._applicationInspectValue = this.applicationConfiguration ? this.applicationConfiguration.inspect(this.key) : null;
}
return this._applicationInspectValue;
}
get applicationValue() {
return this.applicationInspectValue?.merged;
}
get application() {
return this.applicationInspectValue?.value !== undefined || this.applicationInspectValue?.override !== undefined ? { value: this.applicationInspectValue.value, override: this.applicationInspectValue.override } : undefined;
}
_userInspectValue;
get userInspectValue() {
if (!this._userInspectValue) {
this._userInspectValue = this.userConfiguration.inspect(this.key, this.overrides.overrideIdentifier);
}
return this._userInspectValue;
}
get userValue() {
return this.userInspectValue.merged;
}
get user() {
return this.userInspectValue.value !== undefined || this.userInspectValue.override !== undefined ? { value: this.userInspectValue.value, override: this.userInspectValue.override } : undefined;
}
_userLocalInspectValue;
get userLocalInspectValue() {
if (!this._userLocalInspectValue) {
this._userLocalInspectValue = this.localUserConfiguration.inspect(this.key, this.overrides.overrideIdentifier);
}
return this._userLocalInspectValue;
}
get userLocalValue() {
return this.userLocalInspectValue.merged;
}
get userLocal() {
return this.userLocalInspectValue.value !== undefined || this.userLocalInspectValue.override !== undefined ? { value: this.userLocalInspectValue.value, override: this.userLocalInspectValue.override } : undefined;
}
_userRemoteInspectValue;
get userRemoteInspectValue() {
if (!this._userRemoteInspectValue) {
this._userRemoteInspectValue = this.remoteUserConfiguration.inspect(this.key, this.overrides.overrideIdentifier);
}
return this._userRemoteInspectValue;
}
get userRemoteValue() {
return this.userRemoteInspectValue.merged;
}
get userRemote() {
return this.userRemoteInspectValue.value !== undefined || this.userRemoteInspectValue.override !== undefined ? { value: this.userRemoteInspectValue.value, override: this.userRemoteInspectValue.override } : undefined;
}
_workspaceInspectValue;
get workspaceInspectValue() {
if (this._workspaceInspectValue === undefined) {
this._workspaceInspectValue = this.workspaceConfiguration ? this.workspaceConfiguration.inspect(this.key, this.overrides.overrideIdentifier) : null;
}
return this._workspaceInspectValue;
}
get workspaceValue() {
return this.workspaceInspectValue?.merged;
}
get workspace() {
return this.workspaceInspectValue?.value !== undefined || this.workspaceInspectValue?.override !== undefined ? { value: this.workspaceInspectValue.value, override: this.workspaceInspectValue.override } : undefined;
}
_workspaceFolderInspectValue;
get workspaceFolderInspectValue() {
if (this._workspaceFolderInspectValue === undefined) {
this._workspaceFolderInspectValue = this.folderConfigurationModel ? this.folderConfigurationModel.inspect(this.key, this.overrides.overrideIdentifier) : null;
}
return this._workspaceFolderInspectValue;
}
get workspaceFolderValue() {
return this.workspaceFolderInspectValue?.merged;
}
get workspaceFolder() {
return this.workspaceFolderInspectValue?.value !== undefined || this.workspaceFolderInspectValue?.override !== undefined ? { value: this.workspaceFolderInspectValue.value, override: this.workspaceFolderInspectValue.override } : undefined;
}
get memoryValue() {
return this.memoryInspectValue.merged;
}
get memory() {
return this.memoryInspectValue.value !== undefined || this.memoryInspectValue.override !== undefined ? { value: this.memoryInspectValue.value, override: this.memoryInspectValue.override } : undefined;
}
}
export class Configuration {
_defaultConfiguration;
_policyConfiguration;
_applicationConfiguration;
_localUserConfiguration;
_remoteUserConfiguration;
_workspaceConfiguration;
_folderConfigurations;
_memoryConfiguration;
_memoryConfigurationByResource;
_freeze;
_workspaceConsolidatedConfiguration = null;
_foldersConsolidatedConfigurations = new ResourceMap();
constructor(_defaultConfiguration, _policyConfiguration, _applicationConfiguration, _localUserConfiguration, _remoteUserConfiguration = new ConfigurationModel(), _workspaceConfiguration = new ConfigurationModel(), _folderConfigurations = new ResourceMap(), _memoryConfiguration = new ConfigurationModel(), _memoryConfigurationByResource = new ResourceMap(), _freeze = true) {
this._defaultConfiguration = _defaultConfiguration;
this._policyConfiguration = _policyConfiguration;
this._applicationConfiguration = _applicationConfiguration;
this._localUserConfiguration = _localUserConfiguration;
this._remoteUserConfiguration = _remoteUserConfiguration;
this._workspaceConfiguration = _workspaceConfiguration;
this._folderConfigurations = _folderConfigurations;
this._memoryConfiguration = _memoryConfiguration;
this._memoryConfigurationByResource = _memoryConfigurationByResource;
this._freeze = _freeze;
}
getValue(section, overrides, workspace) {
const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(section, overrides, workspace);
return consolidateConfigurationModel.getValue(section);
}
updateValue(key, value, overrides = {}) {
let memoryConfiguration;
if (overrides.resource) {
memoryConfiguration = this._memoryConfigurationByResource.get(overrides.resource);
if (!memoryConfiguration) {
memoryConfiguration = new ConfigurationModel();
this._memoryConfigurationByResource.set(overrides.resource, memoryConfiguration);
}
}
else {
memoryConfiguration = this._memoryConfiguration;
}
if (value === undefined) {
memoryConfiguration.removeValue(key);
}
else {
memoryConfiguration.setValue(key, value);
}
if (!overrides.resource) {
this._workspaceConsolidatedConfiguration = null;
}
}
inspect(key, overrides, workspace) {
const consolidateConfigurationModel = this.getConsolidatedConfigurationModel(key, overrides, workspace);
const folderConfigurationModel = this.getFolderConfigurationModelForResource(overrides.resource, workspace);
const memoryConfigurationModel = overrides.resource ? this._memoryConfigurationByResource.get(overrides.resource) || this._memoryConfiguration : this._memoryConfiguration;
const overrideIdentifiers = new Set();
for (const override of consolidateConfigurationModel.overrides) {
for (const overrideIdentifier of override.identifiers) {
if (consolidateConfigurationModel.getOverrideValue(key, overrideIdentifier) !== undefined) {
overrideIdentifiers.add(overrideIdentifier);
}
}
}
return new ConfigurationInspectValue(key, overrides, consolidateConfigurationModel.getValue(key), overrideIdentifiers.size ? [...overrideIdentifiers] : undefined, this._defaultConfiguration, this._policyConfiguration.isEmpty() ? undefined : this._policyConfiguration.freeze(), this.applicationConfiguration.isEmpty() ? undefined : this.applicationConfiguration.freeze(), this.userConfiguration.freeze(), this.localUserConfiguration.freeze(), this.remoteUserConfiguration.freeze(), workspace ? this._workspaceConfiguration.freeze() : undefined, folderConfigurationModel ? folderConfigurationModel.freeze() : undefined, memoryConfigurationModel.inspect(key, overrides.overrideIdentifier));
}
keys(workspace) {
const folderConfigurationModel = this.getFolderConfigurationModelForResource(undefined, workspace);
return {
default: this._defaultConfiguration.freeze().keys,
user: this.userConfiguration.freeze().keys,
workspace: this._workspaceConfiguration.freeze().keys,
workspaceFolder: folderConfigurationModel ? folderConfigurationModel.freeze().keys : []
};
}
updateDefaultConfiguration(defaultConfiguration) {
this._defaultConfiguration = defaultConfiguration;
this._workspaceConsolidatedConfiguration = null;
this._foldersConsolidatedConfigurations.clear();
}
updatePolicyConfiguration(policyConfiguration) {
this._policyConfiguration = policyConfiguration;
}
updateApplicationConfiguration(applicationConfiguration) {
this._applicationConfiguration = applicationConfiguration;
this._workspaceConsolidatedConfiguration = null;
this._foldersConsolidatedConfigurations.clear();
}
updateLocalUserConfiguration(localUserConfiguration) {
this._localUserConfiguration = localUserConfiguration;
this._userConfiguration = null;
this._workspaceConsolidatedConfiguration = null;
this._foldersConsolidatedConfigurations.clear();
}
updateRemoteUserConfiguration(remoteUserConfiguration) {
this._remoteUserConfiguration = remoteUserConfiguration;
this._userConfiguration = null;
this._workspaceConsolidatedConfiguration = null;
this._foldersConsolidatedConfigurations.clear();
}
updateWorkspaceConfiguration(workspaceConfiguration) {
this._workspaceConfiguration = workspaceConfiguration;
this._workspaceConsolidatedConfiguration = null;
this._foldersConsolidatedConfigurations.clear();
}
updateFolderConfiguration(resource, configuration) {
this._folderConfigurations.set(resource, configuration);
this._foldersConsolidatedConfigurations.delete(resource);
}
deleteFolderConfiguration(resource) {
this.folderConfigurations.delete(resource);
this._foldersConsolidatedConfigurations.delete(resource);
}
compareAndUpdateDefaultConfiguration(defaults, keys) {
const overrides = [];
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 = [
...toKeys.filter(key => fromKeys.indexOf(key) === -1),
...fromKeys.filter(key => toKeys.indexOf(key) === -1),
...fromKeys.filter(key => !objects.equals(this._defaultConfiguration.override(overrideIdentifier).getValue(key), defaults.override(overrideIdentifier).getValue(key)))
];
overrides.push([overrideIdentifier, keys]);
}
}
this.updateDefaultConfiguration(defaults);
return { keys, overrides };
}
compareAndUpdatePolicyConfiguration(policyConfiguration) {
const { added, updated, removed } = compare(this._policyConfiguration, policyConfiguration);
const keys = [...added, ...updated, ...removed];
if (keys.length) {
this.updatePolicyConfiguration(policyConfiguration);
}
return { keys, overrides: [] };
}
compareAndUpdateApplicationConfiguration(application) {
const { added, updated, removed, overrides } = compare(this.applicationConfiguration, application);
const keys = [...added, ...updated, ...removed];
if (keys.length) {
this.updateApplicationConfiguration(application);
}
return { keys, overrides };
}
compareAndUpdateLocalUserConfiguration(user) {
const { added, updated, removed, overrides } = compare(this.localUserConfiguration, user);
const keys = [...added, ...updated, ...removed];
if (keys.length) {
this.updateLocalUserConfiguration(user);
}
return { keys, overrides };
}
compareAndUpdateRemoteUserConfiguration(user) {
const { added, updated, removed, overrides } = compare(this.remoteUserConfiguration, user);
const keys = [...added, ...updated, ...removed];
if (keys.length) {
this.updateRemoteUserConfiguration(user);
}
return { keys, overrides };
}
compareAndUpdateWorkspaceConfiguration(workspaceConfiguration) {
const { added, updated, removed, overrides } = compare(this.workspaceConfiguration, workspaceConfiguration);
const keys = [...added, ...updated, ...removed];
if (keys.length) {
this.updateWorkspaceConfiguration(workspaceConfiguration);
}
return { keys, overrides };
}
compareAndUpdateFolderConfiguration(resource, folderConfiguration) {
const currentFolderConfiguration = this.folderConfigurations.get(resource);
const { added, updated, removed, overrides } = compare(currentFolderConfiguration, folderConfiguration);
const keys = [...added, ...updated, ...removed];
if (keys.length || !currentFolderConfiguration) {
this.updateFolderConfiguration(resource, folderConfiguration);
}
return { keys, overrides };
}
compareAndDeleteFolderConfiguration(folder) {
const folderConfig = this.folderConfigurations.get(folder);
if (!folderConfig) {
throw new Error('Unknown folder');
}
this.deleteFolderConfiguration(folder);
const { added, updated, removed, overrides } = compare(folderConfig, undefined);
return { keys: [...added, ...updated, ...removed], overrides };
}
get defaults() {
return this._defaultConfiguration;
}
get applicationConfiguration() {
return this._applicationConfiguration;
}
_userConfiguration = null;
get userConfiguration() {
if (!this._userConfiguration) {
this._userConfiguration = this._remoteUserConfiguration.isEmpty() ? this._localUserConfiguration : this._localUserConfiguration.merge(this._remoteUserConfiguration);
if (this._freeze) {
this._userConfiguration.freeze();
}
}
return this._userConfiguration;
}
get localUserConfiguration() {
return this._localUserConfiguration;
}
get remoteUserConfiguration() {
return this._remoteUserConfiguration;
}
get workspaceConfiguration() {
return this._workspaceConfiguration;
}
get folderConfigurations() {
return this._folderConfigurations;
}
getConsolidatedConfigurationModel(section, overrides, workspace) {
let configurationModel = this.getConsolidatedConfigurationModelForResource(overrides, workspace);
if (overrides.overrideIdentifier) {
configurationModel = configurationModel.override(overrides.overrideIdentifier);
}
if (!this._policyConfiguration.isEmpty() && this._policyConfiguration.getValue(section) !== undefined) {
configurationModel = configurationModel.merge(this._policyConfiguration);
}
return configurationModel;
}
getConsolidatedConfigurationModelForResource({ resource }, workspace) {
let consolidateConfiguration = this.getWorkspaceConsolidatedConfiguration();
if (workspace && resource) {
const root = workspace.getFolder(resource);
if (root) {
consolidateConfiguration = this.getFolderConsolidatedConfiguration(root.uri) || consolidateConfiguration;
}
const memoryConfigurationForResource = this._memoryConfigurationByResource.get(resource);
if (memoryConfigurationForResource) {
consolidateConfiguration = consolidateConfiguration.merge(memoryConfigurationForResource);
}
}
return consolidateConfiguration;
}
getWorkspaceConsolidatedConfiguration() {
if (!this._workspaceConsolidatedConfiguration) {
this._workspaceConsolidatedConfiguration = this._defaultConfiguration.merge(this.applicationConfiguration, this.userConfiguration, this._workspaceConfiguration, this._memoryConfiguration);
if (this._freeze) {
this._workspaceConfiguration = this._workspaceConfiguration.freeze();
}
}
return this._workspaceConsolidatedConfiguration;
}
getFolderConsolidatedConfiguration(folder) {
let folderConsolidatedConfiguration = this._foldersConsolidatedConfigurations.get(folder);
if (!folderConsolidatedConfiguration) {
const workspaceConsolidateConfiguration = this.getWorkspaceConsolidatedConfiguration();
const folderConfiguration = this._folderConfigurations.get(folder);
if (folderConfiguration) {
folderConsolidatedConfiguration = workspaceConsolidateConfiguration.merge(folderConfiguration);
if (this._freeze) {
folderConsolidatedConfiguration = folderConsolidatedConfiguration.freeze();
}
this._foldersConsolidatedConfigurations.set(folder, folderConsolidatedConfiguration);
}
else {
folderConsolidatedConfiguration = workspaceConsolidateConfiguration;
}
}
return folderConsolidatedConfiguration;
}
getFolderConfigurationModelForResource(resource, workspace) {
if (workspace && resource) {
const root = workspace.getFolder(resource);
if (root) {
return this._folderConfigurations.get(root.uri);
}
}
return undefined;
}
toData() {
return {
defaults: {
contents: this._defaultConfiguration.contents,
overrides: this._defaultConfiguration.overrides,
keys: this._defaultConfiguration.keys
},
policy: {
contents: this._policyConfiguration.contents,
overrides: this._policyConfiguration.overrides,
keys: this._policyConfiguration.keys
},
application: {
contents: this.applicationConfiguration.contents,
overrides: this.applicationConfiguration.overrides,
keys: this.applicationConfiguration.keys
},
user: {
contents: this.userConfiguration.contents,
overrides: this.userConfiguration.overrides,
keys: this.userConfiguration.keys
},
workspace: {
contents: this._workspaceConfiguration.contents,
overrides: this._workspaceConfiguration.overrides,
keys: this._workspaceConfiguration.keys
},
folders: [...this._folderConfigurations.keys()].reduce((result, folder) => {
const { contents, overrides, keys } = this._folderConfigurations.get(folder);
result.push([folder, { contents, overrides, keys }]);
return result;
}, [])
};
}
allKeys() {
const keys = new Set();
this._defaultConfiguration.freeze().keys.forEach(key => keys.add(key));
this.userConfiguration.freeze().keys.forEach(key => keys.add(key));
this._workspaceConfiguration.freeze().keys.forEach(key => keys.add(key));
this._folderConfigurations.forEach(folderConfiguraiton => folderConfiguraiton.freeze().keys.forEach(key => keys.add(key)));
return [...keys.values()];
}
allOverrideIdentifiers() {
const keys = new Set();
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()];
}
getAllKeysForOverrideIdentifier(overrideIdentifier) {
const keys = new Set();
this._defaultConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
this.userConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
this._workspaceConfiguration.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key));
this._folderConfigurations.forEach(folderConfiguraiton => folderConfiguraiton.getKeysForOverrideIdentifier(overrideIdentifier).forEach(key => keys.add(key)));
return [...keys.values()];
}
static parse(data) {
const defaultConfiguration = this.parseConfigurationModel(data.defaults);
const policyConfiguration = this.parseConfigurationModel(data.policy);
const applicationConfiguration = this.parseConfigurationModel(data.application);
const userConfiguration = this.parseConfigurationModel(data.user);
const workspaceConfiguration = this.parseConfigurationModel(data.workspace);
const folders = data.folders.reduce((result, value) => {
result.set(URI.revive(value[0]), this.parseConfigurationModel(value[1]));
return result;
}, new ResourceMap());
return new Configuration(defaultConfiguration, policyConfiguration, applicationConfiguration, userConfiguration, new ConfigurationModel(), workspaceConfiguration, folders, new ConfigurationModel(), new ResourceMap(), false);
}
static parseConfigurationModel(model) {
return new ConfigurationModel(model.contents, model.keys, model.overrides).freeze();
}
}
export function mergeChanges(...changes) {
if (changes.length === 0) {
return { keys: [], overrides: [] };
}
if (changes.length === 1) {
return changes[0];
}
const keysSet = new Set();
const overridesMap = new Map();
for (const change of changes) {
change.keys.forEach(key => keysSet.add(key));
change.overrides.forEach(([identifier, keys]) => {
const result = getOrSet(overridesMap, identifier, new Set());
keys.forEach(key => result.add(key));
});
}
const overrides = [];
overridesMap.forEach((keys, identifier) => overrides.push([identifier, [...keys.values()]]));
return { keys: [...keysSet.values()], overrides };
}
export class ConfigurationChangeEvent {
change;
previous;
currentConfiguraiton;
currentWorkspace;
_marker = '\n';
_markerCode1 = this._marker.charCodeAt(0);
_markerCode2 = '.'.charCodeAt(0);
_affectsConfigStr;
affectedKeys = new Set();
source;
sourceConfig;
constructor(change, previous, currentConfiguraiton, currentWorkspace) {
this.change = change;
this.previous = previous;
this.currentConfiguraiton = currentConfiguraiton;
this.currentWorkspace = currentWorkspace;
for (const key of change.keys) {
this.affectedKeys.add(key);
}
for (const [, keys] of change.overrides) {
for (const key of keys) {
this.affectedKeys.add(key);
}
}
// Example: '\nfoo.bar\nabc.def\n'
this._affectsConfigStr = this._marker;
for (const key of this.affectedKeys) {
this._affectsConfigStr += key + this._marker;
}
}
_previousConfiguration = undefined;
get previousConfiguration() {
if (!this._previousConfiguration && this.previous) {
this._previousConfiguration = Configuration.parse(this.previous.data);
}
return this._previousConfiguration;
}
affectsConfiguration(section, overrides) {
// we have one large string with all keys that have changed. we pad (marker) the section
// and check that either find it padded or before a segment character
const needle = this._marker + section;
const idx = this._affectsConfigStr.indexOf(needle);
if (idx < 0) {
// NOT: (marker + section)
return false;
}
const pos = idx + needle.length;
if (pos >= this._affectsConfigStr.length) {
return false;
}
const code = this._affectsConfigStr.charCodeAt(pos);
if (code !== this._markerCode1 && code !== this._markerCode2) {
// NOT: section + (marker | segment)
return false;
}
if (overrides) {
const value1 = this.previousConfiguration ? this.previousConfiguration.getValue(section, overrides, this.previous?.workspace) : undefined;
const value2 = this.currentConfiguraiton.getValue(section, overrides, this.currentWorkspace);
return !objects.equals(value1, value2);
}
return true;
}
}
function compare(from, to) {
const { added, removed, updated } = compareConfigurationContents(to?.rawConfiguration, from?.rawConfiguration);
const overrides = [];
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, from) {
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 = [];
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 };
}