cae8005c20db9ee3745369b5fe0c8f14e1f2e551be912549dacf00179e7b874c3a5ae3d12da5553d6845b7c790681b74ed95fdd2db72e0a1088a6fd28964e7 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  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 { RunOnceScheduler } from '../../../base/common/async.js';
  6. import { Codicon, CSSIcon } from '../../../base/common/codicons.js';
  7. import { Emitter } from '../../../base/common/event.js';
  8. import { isString } from '../../../base/common/types.js';
  9. import { URI } from '../../../base/common/uri.js';
  10. import { localize } from '../../../nls.js';
  11. import { Extensions as JSONExtensions } from '../../jsonschemas/common/jsonContributionRegistry.js';
  12. import * as platform from '../../registry/common/platform.js';
  13. import { ThemeIcon } from './themeService.js';
  14. // icon registry
  15. export const Extensions = {
  16. IconContribution: 'base.contributions.icons'
  17. };
  18. export var IconContribution;
  19. (function (IconContribution) {
  20. function getDefinition(contribution, registry) {
  21. let definition = contribution.defaults;
  22. while (ThemeIcon.isThemeIcon(definition)) {
  23. const c = iconRegistry.getIcon(definition.id);
  24. if (!c) {
  25. return undefined;
  26. }
  27. definition = c.defaults;
  28. }
  29. return definition;
  30. }
  31. IconContribution.getDefinition = getDefinition;
  32. })(IconContribution || (IconContribution = {}));
  33. export var IconFontDefinition;
  34. (function (IconFontDefinition) {
  35. function toJSONObject(iconFont) {
  36. return {
  37. weight: iconFont.weight,
  38. style: iconFont.style,
  39. src: iconFont.src.map(s => ({ format: s.format, location: s.location.toString() }))
  40. };
  41. }
  42. IconFontDefinition.toJSONObject = toJSONObject;
  43. function fromJSONObject(json) {
  44. const stringOrUndef = (s) => isString(s) ? s : undefined;
  45. if (json && Array.isArray(json.src) && json.src.every((s) => isString(s.format) && isString(s.location))) {
  46. return {
  47. weight: stringOrUndef(json.weight),
  48. style: stringOrUndef(json.style),
  49. src: json.src.map((s) => ({ format: s.format, location: URI.parse(s.location) }))
  50. };
  51. }
  52. return undefined;
  53. }
  54. IconFontDefinition.fromJSONObject = fromJSONObject;
  55. })(IconFontDefinition || (IconFontDefinition = {}));
  56. class IconRegistry {
  57. constructor() {
  58. this._onDidChange = new Emitter();
  59. this.onDidChange = this._onDidChange.event;
  60. this.iconSchema = {
  61. definitions: {
  62. icons: {
  63. type: 'object',
  64. properties: {
  65. fontId: { type: 'string', description: localize('iconDefinition.fontId', 'The id of the font to use. If not set, the font that is defined first is used.') },
  66. fontCharacter: { type: 'string', description: localize('iconDefinition.fontCharacter', 'The font character associated with the icon definition.') }
  67. },
  68. additionalProperties: false,
  69. defaultSnippets: [{ body: { fontCharacter: '\\\\e030' } }]
  70. }
  71. },
  72. type: 'object',
  73. properties: {}
  74. };
  75. this.iconReferenceSchema = { type: 'string', pattern: `^${CSSIcon.iconNameExpression}$`, enum: [], enumDescriptions: [] };
  76. this.iconsById = {};
  77. this.iconFontsById = {};
  78. }
  79. registerIcon(id, defaults, description, deprecationMessage) {
  80. const existing = this.iconsById[id];
  81. if (existing) {
  82. if (description && !existing.description) {
  83. existing.description = description;
  84. this.iconSchema.properties[id].markdownDescription = `${description} $(${id})`;
  85. const enumIndex = this.iconReferenceSchema.enum.indexOf(id);
  86. if (enumIndex !== -1) {
  87. this.iconReferenceSchema.enumDescriptions[enumIndex] = description;
  88. }
  89. this._onDidChange.fire();
  90. }
  91. return existing;
  92. }
  93. const iconContribution = { id, description, defaults, deprecationMessage };
  94. this.iconsById[id] = iconContribution;
  95. const propertySchema = { $ref: '#/definitions/icons' };
  96. if (deprecationMessage) {
  97. propertySchema.deprecationMessage = deprecationMessage;
  98. }
  99. if (description) {
  100. propertySchema.markdownDescription = `${description}: $(${id})`;
  101. }
  102. this.iconSchema.properties[id] = propertySchema;
  103. this.iconReferenceSchema.enum.push(id);
  104. this.iconReferenceSchema.enumDescriptions.push(description || '');
  105. this._onDidChange.fire();
  106. return { id };
  107. }
  108. getIcons() {
  109. return Object.keys(this.iconsById).map(id => this.iconsById[id]);
  110. }
  111. getIcon(id) {
  112. return this.iconsById[id];
  113. }
  114. getIconSchema() {
  115. return this.iconSchema;
  116. }
  117. toString() {
  118. const sorter = (i1, i2) => {
  119. return i1.id.localeCompare(i2.id);
  120. };
  121. const classNames = (i) => {
  122. while (ThemeIcon.isThemeIcon(i.defaults)) {
  123. i = this.iconsById[i.defaults.id];
  124. }
  125. return `codicon codicon-${i ? i.id : ''}`;
  126. };
  127. const reference = [];
  128. reference.push(`| preview | identifier | default codicon ID | description`);
  129. reference.push(`| ----------- | --------------------------------- | --------------------------------- | --------------------------------- |`);
  130. const contributions = Object.keys(this.iconsById).map(key => this.iconsById[key]);
  131. for (const i of contributions.filter(i => !!i.description).sort(sorter)) {
  132. reference.push(`|<i class="${classNames(i)}"></i>|${i.id}|${ThemeIcon.isThemeIcon(i.defaults) ? i.defaults.id : i.id}|${i.description || ''}|`);
  133. }
  134. reference.push(`| preview | identifier `);
  135. reference.push(`| ----------- | --------------------------------- |`);
  136. for (const i of contributions.filter(i => !ThemeIcon.isThemeIcon(i.defaults)).sort(sorter)) {
  137. reference.push(`|<i class="${classNames(i)}"></i>|${i.id}|`);
  138. }
  139. return reference.join('\n');
  140. }
  141. }
  142. const iconRegistry = new IconRegistry();
  143. platform.Registry.add(Extensions.IconContribution, iconRegistry);
  144. export function registerIcon(id, defaults, description, deprecationMessage) {
  145. return iconRegistry.registerIcon(id, defaults, description, deprecationMessage);
  146. }
  147. export function getIconRegistry() {
  148. return iconRegistry;
  149. }
  150. function initialize() {
  151. for (const icon of Codicon.getAll()) {
  152. iconRegistry.registerIcon(icon.id, icon.definition, icon.description);
  153. }
  154. }
  155. initialize();
  156. export const iconsSchemaId = 'vscode://schemas/icons';
  157. const schemaRegistry = platform.Registry.as(JSONExtensions.JSONContribution);
  158. schemaRegistry.registerSchema(iconsSchemaId, iconRegistry.getIconSchema());
  159. const delayer = new RunOnceScheduler(() => schemaRegistry.notifySchemaChanged(iconsSchemaId), 200);
  160. iconRegistry.onDidChange(() => {
  161. if (!delayer.isScheduled()) {
  162. delayer.schedule();
  163. }
  164. });
  165. //setTimeout(_ => console.log(iconRegistry.toString()), 5000);
  166. // common icons
  167. export const widgetClose = registerIcon('widget-close', Codicon.close, localize('widgetClose', 'Icon for the close action in widgets.'));
  168. export const gotoPreviousLocation = registerIcon('goto-previous-location', Codicon.arrowUp, localize('previousChangeIcon', 'Icon for goto previous editor location.'));
  169. export const gotoNextLocation = registerIcon('goto-next-location', Codicon.arrowDown, localize('nextChangeIcon', 'Icon for goto next editor location.'));
  170. export const syncing = ThemeIcon.modify(Codicon.sync, 'spin');
  171. export const spinningLoading = ThemeIcon.modify(Codicon.loading, 'spin');