UNPKG

@theia/core

Version:

Theia is a cloud & desktop IDE framework implemented in TypeScript.

367 lines 18.3 kB
"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