configurationRegistry.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import { distinct } from '../../../base/common/arrays.js';
  6. import { Emitter } from '../../../base/common/event.js';
  7. import * as types from '../../../base/common/types.js';
  8. import * as nls from '../../../nls.js';
  9. import { Extensions as JSONExtensions } from '../../jsonschemas/common/jsonContributionRegistry.js';
  10. import { Registry } from '../../registry/common/platform.js';
  11. export const Extensions = {
  12. Configuration: 'base.contributions.configuration'
  13. };
  14. export const allSettings = { properties: {}, patternProperties: {} };
  15. export const applicationSettings = { properties: {}, patternProperties: {} };
  16. export const machineSettings = { properties: {}, patternProperties: {} };
  17. export const machineOverridableSettings = { properties: {}, patternProperties: {} };
  18. export const windowSettings = { properties: {}, patternProperties: {} };
  19. export const resourceSettings = { properties: {}, patternProperties: {} };
  20. export const resourceLanguageSettingsSchemaId = 'vscode://schemas/settings/resourceLanguage';
  21. const contributionRegistry = Registry.as(JSONExtensions.JSONContribution);
  22. class ConfigurationRegistry {
  23. constructor() {
  24. this.overrideIdentifiers = new Set();
  25. this._onDidSchemaChange = new Emitter();
  26. this._onDidUpdateConfiguration = new Emitter();
  27. this.configurationDefaultsOverrides = new Map();
  28. this.defaultLanguageConfigurationOverridesNode = {
  29. id: 'defaultOverrides',
  30. title: nls.localize('defaultLanguageConfigurationOverrides.title', "Default Language Configuration Overrides"),
  31. properties: {}
  32. };
  33. this.configurationContributors = [this.defaultLanguageConfigurationOverridesNode];
  34. this.resourceLanguageSettingsSchema = { properties: {}, patternProperties: {}, additionalProperties: false, errorMessage: 'Unknown editor configuration setting', allowTrailingCommas: true, allowComments: true };
  35. this.configurationProperties = {};
  36. this.excludedConfigurationProperties = {};
  37. contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
  38. this.registerOverridePropertyPatternKey();
  39. }
  40. registerConfiguration(configuration, validate = true) {
  41. this.registerConfigurations([configuration], validate);
  42. }
  43. registerConfigurations(configurations, validate = true) {
  44. const properties = this.doRegisterConfigurations(configurations, validate);
  45. contributionRegistry.registerSchema(resourceLanguageSettingsSchemaId, this.resourceLanguageSettingsSchema);
  46. this._onDidSchemaChange.fire();
  47. this._onDidUpdateConfiguration.fire({ properties });
  48. }
  49. registerDefaultConfigurations(configurationDefaults) {
  50. var _a;
  51. const properties = [];
  52. const overrideIdentifiers = [];
  53. for (const { overrides, source } of configurationDefaults) {
  54. for (const key in overrides) {
  55. properties.push(key);
  56. if (OVERRIDE_PROPERTY_REGEX.test(key)) {
  57. const defaultValue = Object.assign(Object.assign({}, (((_a = this.configurationDefaultsOverrides.get(key)) === null || _a === void 0 ? void 0 : _a.value) || {})), overrides[key]);
  58. this.configurationDefaultsOverrides.set(key, { source, value: defaultValue });
  59. const property = {
  60. type: 'object',
  61. default: defaultValue,
  62. description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for {0} language.", key),
  63. $ref: resourceLanguageSettingsSchemaId,
  64. defaultDefaultValue: defaultValue,
  65. source: types.isString(source) ? undefined : source,
  66. };
  67. overrideIdentifiers.push(...overrideIdentifiersFromKey(key));
  68. this.configurationProperties[key] = property;
  69. this.defaultLanguageConfigurationOverridesNode.properties[key] = property;
  70. }
  71. else {
  72. this.configurationDefaultsOverrides.set(key, { value: overrides[key], source });
  73. const property = this.configurationProperties[key];
  74. if (property) {
  75. this.updatePropertyDefaultValue(key, property);
  76. this.updateSchema(key, property);
  77. }
  78. }
  79. }
  80. }
  81. this.registerOverrideIdentifiers(overrideIdentifiers);
  82. this._onDidSchemaChange.fire();
  83. this._onDidUpdateConfiguration.fire({ properties, defaultsOverrides: true });
  84. }
  85. registerOverrideIdentifiers(overrideIdentifiers) {
  86. for (const overrideIdentifier of overrideIdentifiers) {
  87. this.overrideIdentifiers.add(overrideIdentifier);
  88. }
  89. this.updateOverridePropertyPatternKey();
  90. }
  91. doRegisterConfigurations(configurations, validate) {
  92. const properties = [];
  93. configurations.forEach(configuration => {
  94. properties.push(...this.validateAndRegisterProperties(configuration, validate, configuration.extensionInfo, configuration.restrictedProperties)); // fills in defaults
  95. this.configurationContributors.push(configuration);
  96. this.registerJSONConfiguration(configuration);
  97. });
  98. return properties;
  99. }
  100. validateAndRegisterProperties(configuration, validate = true, extensionInfo, restrictedProperties, scope = 3 /* WINDOW */) {
  101. scope = types.isUndefinedOrNull(configuration.scope) ? scope : configuration.scope;
  102. let propertyKeys = [];
  103. let properties = configuration.properties;
  104. if (properties) {
  105. for (let key in properties) {
  106. if (validate && validateProperty(key)) {
  107. delete properties[key];
  108. continue;
  109. }
  110. const property = properties[key];
  111. property.source = extensionInfo;
  112. // update default value
  113. property.defaultDefaultValue = properties[key].default;
  114. this.updatePropertyDefaultValue(key, property);
  115. // update scope
  116. if (OVERRIDE_PROPERTY_REGEX.test(key)) {
  117. property.scope = undefined; // No scope for overridable properties `[${identifier}]`
  118. }
  119. else {
  120. property.scope = types.isUndefinedOrNull(property.scope) ? scope : property.scope;
  121. property.restricted = types.isUndefinedOrNull(property.restricted) ? !!(restrictedProperties === null || restrictedProperties === void 0 ? void 0 : restrictedProperties.includes(key)) : property.restricted;
  122. }
  123. // Add to properties maps
  124. // Property is included by default if 'included' is unspecified
  125. if (properties[key].hasOwnProperty('included') && !properties[key].included) {
  126. this.excludedConfigurationProperties[key] = properties[key];
  127. delete properties[key];
  128. continue;
  129. }
  130. else {
  131. this.configurationProperties[key] = properties[key];
  132. }
  133. if (!properties[key].deprecationMessage && properties[key].markdownDeprecationMessage) {
  134. // If not set, default deprecationMessage to the markdown source
  135. properties[key].deprecationMessage = properties[key].markdownDeprecationMessage;
  136. }
  137. propertyKeys.push(key);
  138. }
  139. }
  140. let subNodes = configuration.allOf;
  141. if (subNodes) {
  142. for (let node of subNodes) {
  143. propertyKeys.push(...this.validateAndRegisterProperties(node, validate, extensionInfo, restrictedProperties, scope));
  144. }
  145. }
  146. return propertyKeys;
  147. }
  148. getConfigurationProperties() {
  149. return this.configurationProperties;
  150. }
  151. registerJSONConfiguration(configuration) {
  152. const register = (configuration) => {
  153. let properties = configuration.properties;
  154. if (properties) {
  155. for (const key in properties) {
  156. this.updateSchema(key, properties[key]);
  157. }
  158. }
  159. let subNodes = configuration.allOf;
  160. if (subNodes) {
  161. subNodes.forEach(register);
  162. }
  163. };
  164. register(configuration);
  165. }
  166. updateSchema(key, property) {
  167. allSettings.properties[key] = property;
  168. switch (property.scope) {
  169. case 1 /* APPLICATION */:
  170. applicationSettings.properties[key] = property;
  171. break;
  172. case 2 /* MACHINE */:
  173. machineSettings.properties[key] = property;
  174. break;
  175. case 6 /* MACHINE_OVERRIDABLE */:
  176. machineOverridableSettings.properties[key] = property;
  177. break;
  178. case 3 /* WINDOW */:
  179. windowSettings.properties[key] = property;
  180. break;
  181. case 4 /* RESOURCE */:
  182. resourceSettings.properties[key] = property;
  183. break;
  184. case 5 /* LANGUAGE_OVERRIDABLE */:
  185. resourceSettings.properties[key] = property;
  186. this.resourceLanguageSettingsSchema.properties[key] = property;
  187. break;
  188. }
  189. }
  190. updateOverridePropertyPatternKey() {
  191. for (const overrideIdentifier of this.overrideIdentifiers.values()) {
  192. const overrideIdentifierProperty = `[${overrideIdentifier}]`;
  193. const resourceLanguagePropertiesSchema = {
  194. type: 'object',
  195. description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."),
  196. errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."),
  197. $ref: resourceLanguageSettingsSchemaId,
  198. };
  199. this.updatePropertyDefaultValue(overrideIdentifierProperty, resourceLanguagePropertiesSchema);
  200. allSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
  201. applicationSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
  202. machineSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
  203. machineOverridableSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
  204. windowSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
  205. resourceSettings.properties[overrideIdentifierProperty] = resourceLanguagePropertiesSchema;
  206. }
  207. this._onDidSchemaChange.fire();
  208. }
  209. registerOverridePropertyPatternKey() {
  210. const resourceLanguagePropertiesSchema = {
  211. type: 'object',
  212. description: nls.localize('overrideSettings.defaultDescription', "Configure editor settings to be overridden for a language."),
  213. errorMessage: nls.localize('overrideSettings.errorMessage', "This setting does not support per-language configuration."),
  214. $ref: resourceLanguageSettingsSchemaId,
  215. };
  216. allSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
  217. applicationSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
  218. machineSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
  219. machineOverridableSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
  220. windowSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
  221. resourceSettings.patternProperties[OVERRIDE_PROPERTY_PATTERN] = resourceLanguagePropertiesSchema;
  222. this._onDidSchemaChange.fire();
  223. }
  224. updatePropertyDefaultValue(key, property) {
  225. const configurationdefaultOverride = this.configurationDefaultsOverrides.get(key);
  226. let defaultValue = configurationdefaultOverride === null || configurationdefaultOverride === void 0 ? void 0 : configurationdefaultOverride.value;
  227. let defaultSource = configurationdefaultOverride === null || configurationdefaultOverride === void 0 ? void 0 : configurationdefaultOverride.source;
  228. if (types.isUndefined(defaultValue)) {
  229. defaultValue = property.defaultDefaultValue;
  230. defaultSource = undefined;
  231. }
  232. if (types.isUndefined(defaultValue)) {
  233. defaultValue = getDefaultValue(property.type);
  234. }
  235. property.default = defaultValue;
  236. property.defaultValueSource = defaultSource;
  237. }
  238. }
  239. const OVERRIDE_IDENTIFIER_PATTERN = `\\[([^\\]]+)\\]`;
  240. const OVERRIDE_IDENTIFIER_REGEX = new RegExp(OVERRIDE_IDENTIFIER_PATTERN, 'g');
  241. export const OVERRIDE_PROPERTY_PATTERN = `^(${OVERRIDE_IDENTIFIER_PATTERN})+$`;
  242. export const OVERRIDE_PROPERTY_REGEX = new RegExp(OVERRIDE_PROPERTY_PATTERN);
  243. export function overrideIdentifiersFromKey(key) {
  244. const identifiers = [];
  245. if (OVERRIDE_PROPERTY_REGEX.test(key)) {
  246. let matches = OVERRIDE_IDENTIFIER_REGEX.exec(key);
  247. while (matches === null || matches === void 0 ? void 0 : matches.length) {
  248. const identifier = matches[1].trim();
  249. if (identifier) {
  250. identifiers.push(identifier);
  251. }
  252. matches = OVERRIDE_IDENTIFIER_REGEX.exec(key);
  253. }
  254. }
  255. return distinct(identifiers);
  256. }
  257. export function getDefaultValue(type) {
  258. const t = Array.isArray(type) ? type[0] : type;
  259. switch (t) {
  260. case 'boolean':
  261. return false;
  262. case 'integer':
  263. case 'number':
  264. return 0;
  265. case 'string':
  266. return '';
  267. case 'array':
  268. return [];
  269. case 'object':
  270. return {};
  271. default:
  272. return null;
  273. }
  274. }
  275. const configurationRegistry = new ConfigurationRegistry();
  276. Registry.add(Extensions.Configuration, configurationRegistry);
  277. export function validateProperty(property) {
  278. if (!property.trim()) {
  279. return nls.localize('config.property.empty', "Cannot register an empty property");
  280. }
  281. if (OVERRIDE_PROPERTY_REGEX.test(property)) {
  282. return nls.localize('config.property.languageDefault', "Cannot register '{0}'. This matches property pattern '\\\\[.*\\\\]$' for describing language specific editor settings. Use 'configurationDefaults' contribution.", property);
  283. }
  284. if (configurationRegistry.getConfigurationProperties()[property] !== undefined) {
  285. return nls.localize('config.property.duplicate', "Cannot register '{0}'. This property is already registered.", property);
  286. }
  287. return null;
  288. }