suggestWidgetRenderer.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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. var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
  6. var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
  7. if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
  8. 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;
  9. return c > 3 && r && Object.defineProperty(target, key, r), r;
  10. };
  11. var __param = (this && this.__param) || function (paramIndex, decorator) {
  12. return function (target, key) { decorator(target, key, paramIndex); }
  13. };
  14. var _a;
  15. import { isSafari } from '../../../base/browser/browser.js';
  16. import { $, append, hide, show } from '../../../base/browser/dom.js';
  17. import { IconLabel } from '../../../base/browser/ui/iconLabel/iconLabel.js';
  18. import { flatten } from '../../../base/common/arrays.js';
  19. import { Codicon } from '../../../base/common/codicons.js';
  20. import { Emitter } from '../../../base/common/event.js';
  21. import { createMatches } from '../../../base/common/filters.js';
  22. import { DisposableStore } from '../../../base/common/lifecycle.js';
  23. import { URI } from '../../../base/common/uri.js';
  24. import { EDITOR_FONT_DEFAULTS } from '../../common/config/editorOptions.js';
  25. import { completionKindToCssClass } from '../../common/modes.js';
  26. import { getIconClasses } from '../../common/services/getIconClasses.js';
  27. import { IModelService } from '../../common/services/modelService.js';
  28. import { IModeService } from '../../common/services/modeService.js';
  29. import * as nls from '../../../nls.js';
  30. import { FileKind } from '../../../platform/files/common/files.js';
  31. import { registerIcon } from '../../../platform/theme/common/iconRegistry.js';
  32. import { IThemeService, ThemeIcon } from '../../../platform/theme/common/themeService.js';
  33. import { canExpandCompletionItem } from './suggestWidgetDetails.js';
  34. export function getAriaId(index) {
  35. return `suggest-aria-id:${index}`;
  36. }
  37. export const suggestMoreInfoIcon = registerIcon('suggest-more-info', Codicon.chevronRight, nls.localize('suggestMoreInfoIcon', 'Icon for more information in the suggest widget.'));
  38. const _completionItemColor = new (_a = class ColorExtractor {
  39. extract(item, out) {
  40. if (item.textLabel.match(ColorExtractor._regexStrict)) {
  41. out[0] = item.textLabel;
  42. return true;
  43. }
  44. if (item.completion.detail && item.completion.detail.match(ColorExtractor._regexStrict)) {
  45. out[0] = item.completion.detail;
  46. return true;
  47. }
  48. if (typeof item.completion.documentation === 'string') {
  49. const match = ColorExtractor._regexRelaxed.exec(item.completion.documentation);
  50. if (match && (match.index === 0 || match.index + match[0].length === item.completion.documentation.length)) {
  51. out[0] = match[0];
  52. return true;
  53. }
  54. }
  55. return false;
  56. }
  57. },
  58. _a._regexRelaxed = /(#([\da-fA-F]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))/,
  59. _a._regexStrict = new RegExp(`^${_a._regexRelaxed.source}$`, 'i'),
  60. _a);
  61. let ItemRenderer = class ItemRenderer {
  62. constructor(_editor, _modelService, _modeService, _themeService) {
  63. this._editor = _editor;
  64. this._modelService = _modelService;
  65. this._modeService = _modeService;
  66. this._themeService = _themeService;
  67. this._onDidToggleDetails = new Emitter();
  68. this.onDidToggleDetails = this._onDidToggleDetails.event;
  69. this.templateId = 'suggestion';
  70. }
  71. dispose() {
  72. this._onDidToggleDetails.dispose();
  73. }
  74. renderTemplate(container) {
  75. const data = Object.create(null);
  76. data.disposables = new DisposableStore();
  77. data.root = container;
  78. data.root.classList.add('show-file-icons');
  79. data.icon = append(container, $('.icon'));
  80. data.colorspan = append(data.icon, $('span.colorspan'));
  81. const text = append(container, $('.contents'));
  82. const main = append(text, $('.main'));
  83. data.iconContainer = append(main, $('.icon-label.codicon'));
  84. data.left = append(main, $('span.left'));
  85. data.right = append(main, $('span.right'));
  86. data.iconLabel = new IconLabel(data.left, { supportHighlights: true, supportIcons: true });
  87. data.disposables.add(data.iconLabel);
  88. data.parametersLabel = append(data.left, $('span.signature-label'));
  89. data.qualifierLabel = append(data.left, $('span.qualifier-label'));
  90. data.detailsLabel = append(data.right, $('span.details-label'));
  91. data.readMore = append(data.right, $('span.readMore' + ThemeIcon.asCSSSelector(suggestMoreInfoIcon)));
  92. data.readMore.title = nls.localize('readMore', "Read More");
  93. const configureFont = () => {
  94. const options = this._editor.getOptions();
  95. const fontInfo = options.get(43 /* fontInfo */);
  96. const fontFamily = fontInfo.getMassagedFontFamily(isSafari ? EDITOR_FONT_DEFAULTS.fontFamily : null);
  97. const fontFeatureSettings = fontInfo.fontFeatureSettings;
  98. const fontSize = options.get(106 /* suggestFontSize */) || fontInfo.fontSize;
  99. const lineHeight = options.get(107 /* suggestLineHeight */) || fontInfo.lineHeight;
  100. const fontWeight = fontInfo.fontWeight;
  101. const fontSizePx = `${fontSize}px`;
  102. const lineHeightPx = `${lineHeight}px`;
  103. data.root.style.fontSize = fontSizePx;
  104. data.root.style.fontWeight = fontWeight;
  105. main.style.fontFamily = fontFamily;
  106. main.style.fontFeatureSettings = fontFeatureSettings;
  107. main.style.lineHeight = lineHeightPx;
  108. data.icon.style.height = lineHeightPx;
  109. data.icon.style.width = lineHeightPx;
  110. data.readMore.style.height = lineHeightPx;
  111. data.readMore.style.width = lineHeightPx;
  112. };
  113. configureFont();
  114. data.disposables.add(this._editor.onDidChangeConfiguration(e => {
  115. if (e.hasChanged(43 /* fontInfo */) || e.hasChanged(106 /* suggestFontSize */) || e.hasChanged(107 /* suggestLineHeight */)) {
  116. configureFont();
  117. }
  118. }));
  119. return data;
  120. }
  121. renderElement(element, index, data) {
  122. const { completion } = element;
  123. data.root.id = getAriaId(index);
  124. data.colorspan.style.backgroundColor = '';
  125. const labelOptions = {
  126. labelEscapeNewLines: true,
  127. matches: createMatches(element.score)
  128. };
  129. let color = [];
  130. if (completion.kind === 19 /* Color */ && _completionItemColor.extract(element, color)) {
  131. // special logic for 'color' completion items
  132. data.icon.className = 'icon customcolor';
  133. data.iconContainer.className = 'icon hide';
  134. data.colorspan.style.backgroundColor = color[0];
  135. }
  136. else if (completion.kind === 20 /* File */ && this._themeService.getFileIconTheme().hasFileIcons) {
  137. // special logic for 'file' completion items
  138. data.icon.className = 'icon hide';
  139. data.iconContainer.className = 'icon hide';
  140. const labelClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: element.textLabel }), FileKind.FILE);
  141. const detailClasses = getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: completion.detail }), FileKind.FILE);
  142. labelOptions.extraClasses = labelClasses.length > detailClasses.length ? labelClasses : detailClasses;
  143. }
  144. else if (completion.kind === 23 /* Folder */ && this._themeService.getFileIconTheme().hasFolderIcons) {
  145. // special logic for 'folder' completion items
  146. data.icon.className = 'icon hide';
  147. data.iconContainer.className = 'icon hide';
  148. labelOptions.extraClasses = flatten([
  149. getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: element.textLabel }), FileKind.FOLDER),
  150. getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: completion.detail }), FileKind.FOLDER)
  151. ]);
  152. }
  153. else {
  154. // normal icon
  155. data.icon.className = 'icon hide';
  156. data.iconContainer.className = '';
  157. data.iconContainer.classList.add('suggest-icon', ...completionKindToCssClass(completion.kind).split(' '));
  158. }
  159. if (completion.tags && completion.tags.indexOf(1 /* Deprecated */) >= 0) {
  160. labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['deprecated']);
  161. labelOptions.matches = [];
  162. }
  163. data.iconLabel.setLabel(element.textLabel, undefined, labelOptions);
  164. if (typeof completion.label === 'string') {
  165. data.parametersLabel.textContent = '';
  166. data.detailsLabel.textContent = stripNewLines(completion.detail || '');
  167. data.root.classList.add('string-label');
  168. }
  169. else {
  170. data.parametersLabel.textContent = stripNewLines(completion.label.detail || '');
  171. data.detailsLabel.textContent = stripNewLines(completion.label.description || '');
  172. data.root.classList.remove('string-label');
  173. }
  174. if (this._editor.getOption(105 /* suggest */).showInlineDetails) {
  175. show(data.detailsLabel);
  176. }
  177. else {
  178. hide(data.detailsLabel);
  179. }
  180. if (canExpandCompletionItem(element)) {
  181. data.right.classList.add('can-expand-details');
  182. show(data.readMore);
  183. data.readMore.onmousedown = e => {
  184. e.stopPropagation();
  185. e.preventDefault();
  186. };
  187. data.readMore.onclick = e => {
  188. e.stopPropagation();
  189. e.preventDefault();
  190. this._onDidToggleDetails.fire();
  191. };
  192. }
  193. else {
  194. data.right.classList.remove('can-expand-details');
  195. hide(data.readMore);
  196. data.readMore.onmousedown = null;
  197. data.readMore.onclick = null;
  198. }
  199. }
  200. disposeTemplate(templateData) {
  201. templateData.disposables.dispose();
  202. }
  203. };
  204. ItemRenderer = __decorate([
  205. __param(1, IModelService),
  206. __param(2, IModeService),
  207. __param(3, IThemeService)
  208. ], ItemRenderer);
  209. export { ItemRenderer };
  210. function stripNewLines(str) {
  211. return str.replace(/\r\n|\r|\n/g, '');
  212. }