@theia/core
Version:
Theia is a cloud & desktop IDE framework implemented in TypeScript.
367 lines • 18.3 kB
JavaScript
"use strict";
// *****************************************************************************
// Copyright (C) 2018 Ericsson and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
// *****************************************************************************
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PreferenceSchemaProvider = exports.FrontendApplicationPreferenceConfig = exports.bindPreferenceSchemaProvider = exports.PreferenceContribution = exports.PreferenceDataProperty = exports.PreferenceSchemaProperties = exports.PreferenceSchema = void 0;
const Ajv = require("ajv");
const inversify_1 = require("inversify");
const common_1 = require("../../common");
const preference_scope_1 = require("./preference-scope");
const preference_provider_1 = require("./preference-provider");
const preference_schema_1 = require("../../common/preferences/preference-schema");
Object.defineProperty(exports, "PreferenceSchema", { enumerable: true, get: function () { return preference_schema_1.PreferenceSchema; } });
Object.defineProperty(exports, "PreferenceSchemaProperties", { enumerable: true, get: function () { return preference_schema_1.PreferenceSchemaProperties; } });
Object.defineProperty(exports, "PreferenceDataProperty", { enumerable: true, get: function () { return preference_schema_1.PreferenceDataProperty; } });
const frontend_application_config_provider_1 = require("../frontend-application-config-provider");
const preference_configurations_1 = require("./preference-configurations");
const types_1 = require("../../common/types");
const preference_language_override_service_1 = require("./preference-language-override-service");
/* eslint-disable guard-for-in, @typescript-eslint/no-explicit-any */
exports.PreferenceContribution = Symbol('PreferenceContribution');
function bindPreferenceSchemaProvider(bind) {
(0, preference_configurations_1.bindPreferenceConfigurations)(bind);
bind(PreferenceSchemaProvider).toSelf().inSingletonScope();
bind(preference_language_override_service_1.PreferenceLanguageOverrideService).toSelf().inSingletonScope();
(0, common_1.bindContributionProvider)(bind, exports.PreferenceContribution);
}
exports.bindPreferenceSchemaProvider = bindPreferenceSchemaProvider;
var FrontendApplicationPreferenceConfig;
(function (FrontendApplicationPreferenceConfig) {
function is(config) {
return (0, types_1.isObject)(config.preferences);
}
FrontendApplicationPreferenceConfig.is = is;
})(FrontendApplicationPreferenceConfig = exports.FrontendApplicationPreferenceConfig || (exports.FrontendApplicationPreferenceConfig = {}));
/**
* The {@link PreferenceSchemaProvider} collects all {@link PreferenceContribution}s and combines
* the preference schema provided by these contributions into one collective schema. The preferences which
* are provided by this {@link PreferenceProvider} are derived from this combined schema.
*/
let PreferenceSchemaProvider = class PreferenceSchemaProvider extends preference_provider_1.PreferenceProvider {
constructor() {
super(...arguments);
this.preferences = {};
this.combinedSchema = { properties: {}, patternProperties: {}, allowComments: true, allowTrailingCommas: true, };
this.workspaceSchema = { properties: {}, patternProperties: {}, allowComments: true, allowTrailingCommas: true, };
this.folderSchema = { properties: {}, patternProperties: {}, allowComments: true, allowTrailingCommas: true, };
this.onDidPreferenceSchemaChangedEmitter = new common_1.Emitter();
this.onDidPreferenceSchemaChanged = this.onDidPreferenceSchemaChangedEmitter.event;
this.overridePatternProperties = {
type: 'object',
description: 'Configure editor settings to be overridden for a language.',
errorMessage: 'Unknown Identifier. Use language identifiers',
properties: {},
additionalProperties: false
};
}
fireDidPreferenceSchemaChanged() {
this.onDidPreferenceSchemaChangedEmitter.fire(undefined);
}
init() {
this.readConfiguredPreferences();
this.preferenceContributions.getContributions().forEach(contrib => {
this.doSetSchema(contrib.schema);
});
this.combinedSchema.additionalProperties = false;
this._ready.resolve();
}
/**
* Register a new overrideIdentifier. Existing identifiers are not replaced.
*
* Allows overriding existing values while keeping both values in store.
* For example to store different editor settings, e.g. "[markdown].editor.autoIndent",
* "[json].editor.autoIndent" and "editor.autoIndent"
* @param overrideIdentifier the new overrideIdentifier
*/
registerOverrideIdentifier(overrideIdentifier) {
if (this.preferenceOverrideService.addOverrideIdentifier(overrideIdentifier)) {
this.updateOverridePatternPropertiesKey();
}
}
updateOverridePatternPropertiesKey() {
const oldKey = this.overridePatternPropertiesKey;
const newKey = this.preferenceOverrideService.computeOverridePatternPropertiesKey();
if (oldKey === newKey) {
return;
}
if (oldKey) {
delete this.combinedSchema.patternProperties[oldKey];
}
this.overridePatternPropertiesKey = newKey;
if (newKey) {
this.combinedSchema.patternProperties[newKey] = this.overridePatternProperties;
}
this.fireDidPreferenceSchemaChanged();
}
doUnsetSchema(changes) {
const inverseChanges = [];
for (const change of changes) {
const preferenceName = change.preferenceName;
const overridden = this.preferenceOverrideService.overriddenPreferenceName(preferenceName);
if (overridden) {
delete this.overridePatternProperties.properties[`[${overridden.overrideIdentifier}]`];
this.removePropFromSchemas(`[${overridden.overrideIdentifier}]`);
}
else {
this.removePropFromSchemas(preferenceName);
}
const newValue = change.oldValue;
const oldValue = change.newValue;
const { scope, domain } = change;
const inverseChange = { preferenceName, oldValue, scope, domain };
if (typeof newValue === undefined) {
delete this.preferences[preferenceName];
}
else {
inverseChange.newValue = newValue;
this.preferences[preferenceName] = newValue;
}
inverseChanges.push(inverseChange);
}
return inverseChanges;
}
validateSchema(schema) {
const ajv = new Ajv();
const valid = ajv.validateSchema(schema);
if (!valid) {
const errors = !!ajv.errors ? ajv.errorsText(ajv.errors) : 'unknown validation error';
console.warn('A contributed preference schema has validation issues : ' + errors);
}
}
doSetSchema(schema) {
if (frontend_application_config_provider_1.FrontendApplicationConfigProvider.get().validatePreferencesSchema) {
this.validateSchema(schema);
}
const scope = preference_scope_1.PreferenceScope.Default;
const domain = this.getDomain();
const changes = [];
const defaultScope = preference_schema_1.PreferenceSchema.getDefaultScope(schema);
const overridable = schema.overridable || false;
for (const [preferenceName, rawSchemaProps] of Object.entries(schema.properties)) {
if (this.combinedSchema.properties[preferenceName]) {
console.error('Preference name collision detected in the schema for property: ' + preferenceName);
}
else if (!rawSchemaProps.hasOwnProperty('included') || rawSchemaProps.included) {
const schemaProps = preference_schema_1.PreferenceDataProperty.fromPreferenceSchemaProperty(rawSchemaProps, defaultScope);
if (typeof schemaProps.overridable !== 'boolean' && overridable) {
schemaProps.overridable = true;
}
if (schemaProps.overridable) {
this.overridePatternProperties.properties[preferenceName] = schemaProps;
}
this.updateSchemaProps(preferenceName, schemaProps);
const schemaDefault = this.getDefaultValue(schemaProps);
const configuredDefault = this.getConfiguredDefault(preferenceName);
if (this.preferenceOverrideService.testOverrideValue(preferenceName, schemaDefault)) {
schemaProps.defaultValue = preference_schema_1.PreferenceSchemaProperties.is(configuredDefault)
? preference_provider_1.PreferenceProvider.merge(schemaDefault, configuredDefault)
: schemaDefault;
if (schemaProps.defaultValue && preference_schema_1.PreferenceSchemaProperties.is(schemaProps.defaultValue)) {
for (const overriddenPreferenceName in schemaProps.defaultValue) {
const overrideValue = schemaDefault[overriddenPreferenceName];
const overridePreferenceName = `${preferenceName}.${overriddenPreferenceName}`;
changes.push(this.doSetPreferenceValue(overridePreferenceName, overrideValue, { scope, domain }));
}
}
}
else {
schemaProps.defaultValue = configuredDefault === undefined ? schemaDefault : configuredDefault;
changes.push(this.doSetPreferenceValue(preferenceName, schemaProps.defaultValue, { scope, domain }));
}
}
}
return changes;
}
doSetPreferenceValue(preferenceName, newValue, { scope, domain }) {
const oldValue = this.preferences[preferenceName];
this.preferences[preferenceName] = newValue;
return { preferenceName, oldValue, newValue, scope, domain };
}
getDefaultValue(property) {
if (property.defaultValue !== undefined) {
return property.defaultValue;
}
if (property.default !== undefined) {
return property.default;
}
const type = Array.isArray(property.type) ? property.type[0] : property.type;
switch (type) {
case 'boolean':
return false;
case 'integer':
case 'number':
return 0;
case 'string':
return '';
case 'array':
return [];
case 'object':
return {};
}
// eslint-disable-next-line no-null/no-null
return null;
}
getConfiguredDefault(preferenceName) {
const config = frontend_application_config_provider_1.FrontendApplicationConfigProvider.get();
if (preferenceName && FrontendApplicationPreferenceConfig.is(config) && preferenceName in config.preferences) {
return config.preferences[preferenceName];
}
}
getCombinedSchema() {
return this.combinedSchema;
}
getSchema(scope) {
switch (scope) {
case preference_scope_1.PreferenceScope.Default:
case preference_scope_1.PreferenceScope.User:
return this.combinedSchema;
case preference_scope_1.PreferenceScope.Workspace:
return this.workspaceSchema;
case preference_scope_1.PreferenceScope.Folder:
return this.folderSchema;
}
}
setSchema(schema) {
const changes = this.doSetSchema(schema);
if (!changes.length) {
return common_1.Disposable.NULL;
}
this.fireDidPreferenceSchemaChanged();
this.emitPreferencesChangedEvent(changes);
return common_1.Disposable.create(() => {
const inverseChanges = this.doUnsetSchema(changes);
if (!inverseChanges.length) {
return;
}
this.fireDidPreferenceSchemaChanged();
this.emitPreferencesChangedEvent(inverseChanges);
});
}
getPreferences() {
return this.preferences;
}
async setPreference() {
return false;
}
isValidInScope(preferenceName, scope) {
let property;
const overridden = this.preferenceOverrideService.overriddenPreferenceName(preferenceName);
if (overridden) {
// try from overridden schema
property = this.overridePatternProperties[`[${overridden.overrideIdentifier}]`];
property = property && property[overridden.preferenceName];
if (!property) {
// try from overridden identifier
property = this.overridePatternProperties[overridden.preferenceName];
}
if (!property) {
// try from overridden value
property = this.combinedSchema.properties[overridden.preferenceName];
}
}
else {
property = this.combinedSchema.properties[preferenceName];
}
return property && property.scope >= scope;
}
*getPreferenceNames() {
for (const preferenceName in this.combinedSchema.properties) {
yield preferenceName;
for (const overridePreferenceName of this.getOverridePreferenceNames(preferenceName)) {
yield overridePreferenceName;
}
}
}
getOverridePreferenceNames(preferenceName) {
const preference = this.combinedSchema.properties[preferenceName];
if (preference && preference.overridable) {
return this.preferenceOverrideService.getOverridePreferenceNames(preferenceName);
}
return [][Symbol.iterator]();
}
getSchemaProperty(key) {
return this.combinedSchema.properties[key];
}
updateSchemaProperty(key, property) {
this.updateSchemaProps(key, property);
this.fireDidPreferenceSchemaChanged();
}
updateSchemaProps(key, property) {
this.combinedSchema.properties[key] = property;
switch (property.scope) {
case preference_scope_1.PreferenceScope.Folder:
this.folderSchema.properties[key] = property;
// Fall through. isValidInScope implies that User ⊃ Workspace ⊃ Folder,
// so anything we add to folder should be added to workspace, but not vice versa.
case preference_scope_1.PreferenceScope.Workspace:
this.workspaceSchema.properties[key] = property;
break;
}
}
removePropFromSchemas(key) {
// If we remove a key from combined, it should also be removed from all narrower scopes.
delete this.combinedSchema.properties[key];
delete this.workspaceSchema.properties[key];
delete this.folderSchema.properties[key];
}
readConfiguredPreferences() {
const config = frontend_application_config_provider_1.FrontendApplicationConfigProvider.get();
if (FrontendApplicationPreferenceConfig.is(config)) {
try {
const configuredDefaults = config.preferences;
const parsedDefaults = this.getParsedContent(configuredDefaults);
Object.assign(this.preferences, parsedDefaults);
const scope = preference_scope_1.PreferenceScope.Default;
const domain = this.getDomain();
const changes = Object.keys(this.preferences)
.map((key) => ({ preferenceName: key, oldValue: undefined, newValue: this.preferences[key], scope, domain }));
this.emitPreferencesChangedEvent(changes);
}
catch (e) {
console.error('Failed to load preferences from frontend configuration.', e);
}
}
}
};
__decorate([
(0, inversify_1.inject)(common_1.ContributionProvider),
(0, inversify_1.named)(exports.PreferenceContribution),
__metadata("design:type", Object)
], PreferenceSchemaProvider.prototype, "preferenceContributions", void 0);
__decorate([
(0, inversify_1.inject)(preference_configurations_1.PreferenceConfigurations),
__metadata("design:type", preference_configurations_1.PreferenceConfigurations)
], PreferenceSchemaProvider.prototype, "configurations", void 0);
__decorate([
(0, inversify_1.postConstruct)(),
__metadata("design:type", Function),
__metadata("design:paramtypes", []),
__metadata("design:returntype", void 0)
], PreferenceSchemaProvider.prototype, "init", null);
PreferenceSchemaProvider = __decorate([
(0, inversify_1.injectable)()
], PreferenceSchemaProvider);
exports.PreferenceSchemaProvider = PreferenceSchemaProvider;
//# sourceMappingURL=preference-contribution.js.map