d5d32aa6ba0a0e166af12b88ad23882033efcab19c19700a0392ed9279cf410b5a829097696ee0d4cb62ae5015c0b2c64335d37b9c9b2906559949cc1207e0 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578
  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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  15. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  16. return new (P || (P = Promise))(function (resolve, reject) {
  17. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  18. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  19. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  20. step((generator = generator.apply(thisArg, _arguments || [])).next());
  21. });
  22. };
  23. import { ModifierKeyEmitter } from '../../../../base/browser/dom.js';
  24. import { isNonEmptyArray } from '../../../../base/common/arrays.js';
  25. import { RunOnceScheduler } from '../../../../base/common/async.js';
  26. import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js';
  27. import { onUnexpectedError } from '../../../../base/common/errors.js';
  28. import { DisposableStore, toDisposable } from '../../../../base/common/lifecycle.js';
  29. import { LRUCache } from '../../../../base/common/map.js';
  30. import { assertType } from '../../../../base/common/types.js';
  31. import { URI } from '../../../../base/common/uri.js';
  32. import { DynamicCssRules } from '../../../browser/editorDom.js';
  33. import { EDITOR_FONT_DEFAULTS } from '../../../common/config/editorOptions.js';
  34. import { EditOperation } from '../../../common/core/editOperation.js';
  35. import { Range } from '../../../common/core/range.js';
  36. import * as languages from '../../../common/languages.js';
  37. import { InjectedTextCursorStops } from '../../../common/model.js';
  38. import { ModelDecorationInjectedTextOptions } from '../../../common/model/textModel.js';
  39. import { ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js';
  40. import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
  41. import { ITextModelService } from '../../../common/services/resolverService.js';
  42. import { ClickLinkGesture } from '../../gotoSymbol/browser/link/clickLinkGesture.js';
  43. import { InlayHintAnchor, InlayHintsFragments } from './inlayHints.js';
  44. import { goToDefinitionWithLocation, showGoToContextMenu } from './inlayHintsLocations.js';
  45. import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js';
  46. import { registerSingleton } from '../../../../platform/instantiation/common/extensions.js';
  47. import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
  48. import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js';
  49. import * as colors from '../../../../platform/theme/common/colorRegistry.js';
  50. import { themeColorFromId } from '../../../../platform/theme/common/themeService.js';
  51. // --- hint caching service (per session)
  52. class InlayHintsCache {
  53. constructor() {
  54. this._entries = new LRUCache(50);
  55. }
  56. get(model) {
  57. const key = InlayHintsCache._key(model);
  58. return this._entries.get(key);
  59. }
  60. set(model, value) {
  61. const key = InlayHintsCache._key(model);
  62. this._entries.set(key, value);
  63. }
  64. static _key(model) {
  65. return `${model.uri.toString()}/${model.getVersionId()}`;
  66. }
  67. }
  68. const IInlayHintsCache = createDecorator('IInlayHintsCache');
  69. registerSingleton(IInlayHintsCache, InlayHintsCache, true);
  70. // --- rendered label
  71. export class RenderedInlayHintLabelPart {
  72. constructor(item, index) {
  73. this.item = item;
  74. this.index = index;
  75. }
  76. get part() {
  77. const label = this.item.hint.label;
  78. if (typeof label === 'string') {
  79. return { label };
  80. }
  81. else {
  82. return label[this.index];
  83. }
  84. }
  85. }
  86. class ActiveInlayHintInfo {
  87. constructor(part, hasTriggerModifier) {
  88. this.part = part;
  89. this.hasTriggerModifier = hasTriggerModifier;
  90. }
  91. }
  92. // --- controller
  93. let InlayHintsController = class InlayHintsController {
  94. constructor(_editor, _languageFeaturesService, _featureDebounce, _inlayHintsCache, _commandService, _notificationService, _instaService) {
  95. this._editor = _editor;
  96. this._languageFeaturesService = _languageFeaturesService;
  97. this._inlayHintsCache = _inlayHintsCache;
  98. this._commandService = _commandService;
  99. this._notificationService = _notificationService;
  100. this._instaService = _instaService;
  101. this._disposables = new DisposableStore();
  102. this._sessionDisposables = new DisposableStore();
  103. this._decorationsMetadata = new Map();
  104. this._ruleFactory = new DynamicCssRules(this._editor);
  105. this._activeRenderMode = 0 /* RenderMode.Normal */;
  106. this._debounceInfo = _featureDebounce.for(_languageFeaturesService.inlayHintsProvider, 'InlayHint', { min: 25 });
  107. this._disposables.add(_languageFeaturesService.inlayHintsProvider.onDidChange(() => this._update()));
  108. this._disposables.add(_editor.onDidChangeModel(() => this._update()));
  109. this._disposables.add(_editor.onDidChangeModelLanguage(() => this._update()));
  110. this._disposables.add(_editor.onDidChangeConfiguration(e => {
  111. if (e.hasChanged(129 /* EditorOption.inlayHints */)) {
  112. this._update();
  113. }
  114. }));
  115. this._update();
  116. }
  117. static get(editor) {
  118. var _a;
  119. return (_a = editor.getContribution(InlayHintsController.ID)) !== null && _a !== void 0 ? _a : undefined;
  120. }
  121. dispose() {
  122. this._sessionDisposables.dispose();
  123. this._removeAllDecorations();
  124. this._disposables.dispose();
  125. }
  126. _update() {
  127. this._sessionDisposables.clear();
  128. this._removeAllDecorations();
  129. const options = this._editor.getOption(129 /* EditorOption.inlayHints */);
  130. if (options.enabled === 'off') {
  131. return;
  132. }
  133. const model = this._editor.getModel();
  134. if (!model || !this._languageFeaturesService.inlayHintsProvider.has(model)) {
  135. return;
  136. }
  137. // iff possible, quickly update from cache
  138. const cached = this._inlayHintsCache.get(model);
  139. if (cached) {
  140. this._updateHintsDecorators([model.getFullModelRange()], cached);
  141. }
  142. this._sessionDisposables.add(toDisposable(() => {
  143. // cache items when switching files etc
  144. if (!model.isDisposed()) {
  145. this._cacheHintsForFastRestore(model);
  146. }
  147. }));
  148. let cts;
  149. const watchedProviders = new Set();
  150. const scheduler = new RunOnceScheduler(() => __awaiter(this, void 0, void 0, function* () {
  151. const t1 = Date.now();
  152. cts === null || cts === void 0 ? void 0 : cts.dispose(true);
  153. cts = new CancellationTokenSource();
  154. const listener = model.onWillDispose(() => cts === null || cts === void 0 ? void 0 : cts.cancel());
  155. try {
  156. const myToken = cts.token;
  157. const inlayHints = yield InlayHintsFragments.create(this._languageFeaturesService.inlayHintsProvider, model, this._getHintsRanges(), myToken);
  158. scheduler.delay = this._debounceInfo.update(model, Date.now() - t1);
  159. if (myToken.isCancellationRequested) {
  160. inlayHints.dispose();
  161. return;
  162. }
  163. // listen to provider changes
  164. for (const provider of inlayHints.provider) {
  165. if (typeof provider.onDidChangeInlayHints === 'function' && !watchedProviders.has(provider)) {
  166. watchedProviders.add(provider);
  167. this._sessionDisposables.add(provider.onDidChangeInlayHints(() => {
  168. if (!scheduler.isScheduled()) { // ignore event when request is already scheduled
  169. scheduler.schedule();
  170. }
  171. }));
  172. }
  173. }
  174. this._sessionDisposables.add(inlayHints);
  175. this._updateHintsDecorators(inlayHints.ranges, inlayHints.items);
  176. this._cacheHintsForFastRestore(model);
  177. }
  178. catch (err) {
  179. onUnexpectedError(err);
  180. }
  181. finally {
  182. cts.dispose();
  183. listener.dispose();
  184. }
  185. }), this._debounceInfo.get(model));
  186. this._sessionDisposables.add(scheduler);
  187. this._sessionDisposables.add(toDisposable(() => cts === null || cts === void 0 ? void 0 : cts.dispose(true)));
  188. scheduler.schedule(0);
  189. this._sessionDisposables.add(this._editor.onDidScrollChange((e) => {
  190. // update when scroll position changes
  191. // uses scrollTopChanged has weak heuristic to differenatiate between scrolling due to
  192. // typing or due to "actual" scrolling
  193. if (e.scrollTopChanged || !scheduler.isScheduled()) {
  194. scheduler.schedule();
  195. }
  196. }));
  197. this._sessionDisposables.add(this._editor.onDidChangeModelContent((e) => {
  198. // update less aggressive when typing
  199. const delay = Math.max(scheduler.delay, 1250);
  200. scheduler.schedule(delay);
  201. }));
  202. if (options.enabled === 'on') {
  203. // different "on" modes: always
  204. this._activeRenderMode = 0 /* RenderMode.Normal */;
  205. }
  206. else {
  207. // different "on" modes: offUnlessPressed, or onUnlessPressed
  208. let defaultMode;
  209. let altMode;
  210. if (options.enabled === 'onUnlessPressed') {
  211. defaultMode = 0 /* RenderMode.Normal */;
  212. altMode = 1 /* RenderMode.Invisible */;
  213. }
  214. else {
  215. defaultMode = 1 /* RenderMode.Invisible */;
  216. altMode = 0 /* RenderMode.Normal */;
  217. }
  218. this._activeRenderMode = defaultMode;
  219. this._sessionDisposables.add(ModifierKeyEmitter.getInstance().event(e => {
  220. if (!this._editor.hasModel()) {
  221. return;
  222. }
  223. const newRenderMode = e.altKey && e.ctrlKey ? altMode : defaultMode;
  224. if (newRenderMode !== this._activeRenderMode) {
  225. this._activeRenderMode = newRenderMode;
  226. const model = this._editor.getModel();
  227. const copies = this._copyInlayHintsWithCurrentAnchor(model);
  228. this._updateHintsDecorators([model.getFullModelRange()], copies);
  229. scheduler.schedule(0);
  230. }
  231. }));
  232. }
  233. // mouse gestures
  234. this._sessionDisposables.add(this._installDblClickGesture(() => scheduler.schedule(0)));
  235. this._sessionDisposables.add(this._installLinkGesture());
  236. this._sessionDisposables.add(this._installContextMenu());
  237. }
  238. _installLinkGesture() {
  239. const store = new DisposableStore();
  240. const gesture = store.add(new ClickLinkGesture(this._editor));
  241. // let removeHighlight = () => { };
  242. const sessionStore = new DisposableStore();
  243. store.add(sessionStore);
  244. store.add(gesture.onMouseMoveOrRelevantKeyDown(e => {
  245. const [mouseEvent] = e;
  246. const labelPart = this._getInlayHintLabelPart(mouseEvent);
  247. const model = this._editor.getModel();
  248. if (!labelPart || !model) {
  249. sessionStore.clear();
  250. return;
  251. }
  252. // resolve the item
  253. const cts = new CancellationTokenSource();
  254. sessionStore.add(toDisposable(() => cts.dispose(true)));
  255. labelPart.item.resolve(cts.token);
  256. // render link => when the modifier is pressed and when there is a command or location
  257. this._activeInlayHintPart = labelPart.part.command || labelPart.part.location
  258. ? new ActiveInlayHintInfo(labelPart, mouseEvent.hasTriggerModifier)
  259. : undefined;
  260. const lineNumber = labelPart.item.hint.position.lineNumber;
  261. const range = new Range(lineNumber, 1, lineNumber, model.getLineMaxColumn(lineNumber));
  262. const lineHints = this._getInlineHintsForRange(range);
  263. this._updateHintsDecorators([range], lineHints);
  264. sessionStore.add(toDisposable(() => {
  265. this._activeInlayHintPart = undefined;
  266. this._updateHintsDecorators([range], lineHints);
  267. }));
  268. }));
  269. store.add(gesture.onCancel(() => sessionStore.clear()));
  270. store.add(gesture.onExecute((e) => __awaiter(this, void 0, void 0, function* () {
  271. const label = this._getInlayHintLabelPart(e);
  272. if (label) {
  273. const part = label.part;
  274. if (part.location) {
  275. // location -> execute go to def
  276. this._instaService.invokeFunction(goToDefinitionWithLocation, e, this._editor, part.location);
  277. }
  278. else if (languages.Command.is(part.command)) {
  279. // command -> execute it
  280. yield this._invokeCommand(part.command, label.item);
  281. }
  282. }
  283. })));
  284. return store;
  285. }
  286. _getInlineHintsForRange(range) {
  287. const lineHints = new Set();
  288. for (const data of this._decorationsMetadata.values()) {
  289. if (range.containsRange(data.item.anchor.range)) {
  290. lineHints.add(data.item);
  291. }
  292. }
  293. return Array.from(lineHints);
  294. }
  295. _installDblClickGesture(updateInlayHints) {
  296. return this._editor.onMouseUp((e) => __awaiter(this, void 0, void 0, function* () {
  297. if (e.event.detail !== 2) {
  298. return;
  299. }
  300. const part = this._getInlayHintLabelPart(e);
  301. if (!part) {
  302. return;
  303. }
  304. e.event.preventDefault();
  305. yield part.item.resolve(CancellationToken.None);
  306. if (isNonEmptyArray(part.item.hint.textEdits)) {
  307. const edits = part.item.hint.textEdits.map(edit => EditOperation.replace(Range.lift(edit.range), edit.text));
  308. this._editor.executeEdits('inlayHint.default', edits);
  309. updateInlayHints();
  310. }
  311. }));
  312. }
  313. _installContextMenu() {
  314. return this._editor.onContextMenu((e) => __awaiter(this, void 0, void 0, function* () {
  315. if (!(e.event.target instanceof HTMLElement)) {
  316. return;
  317. }
  318. const part = this._getInlayHintLabelPart(e);
  319. if (part) {
  320. yield this._instaService.invokeFunction(showGoToContextMenu, this._editor, e.event.target, part);
  321. }
  322. }));
  323. }
  324. _getInlayHintLabelPart(e) {
  325. var _a;
  326. if (e.target.type !== 6 /* MouseTargetType.CONTENT_TEXT */) {
  327. return undefined;
  328. }
  329. const options = (_a = e.target.detail.injectedText) === null || _a === void 0 ? void 0 : _a.options;
  330. if (options instanceof ModelDecorationInjectedTextOptions && (options === null || options === void 0 ? void 0 : options.attachedData) instanceof RenderedInlayHintLabelPart) {
  331. return options.attachedData;
  332. }
  333. return undefined;
  334. }
  335. _invokeCommand(command, item) {
  336. var _a;
  337. return __awaiter(this, void 0, void 0, function* () {
  338. try {
  339. yield this._commandService.executeCommand(command.id, ...((_a = command.arguments) !== null && _a !== void 0 ? _a : []));
  340. }
  341. catch (err) {
  342. this._notificationService.notify({
  343. severity: Severity.Error,
  344. source: item.provider.displayName,
  345. message: err
  346. });
  347. }
  348. });
  349. }
  350. _cacheHintsForFastRestore(model) {
  351. const hints = this._copyInlayHintsWithCurrentAnchor(model);
  352. this._inlayHintsCache.set(model, hints);
  353. }
  354. // return inlay hints but with an anchor that reflects "updates"
  355. // that happened after receiving them, e.g adding new lines before a hint
  356. _copyInlayHintsWithCurrentAnchor(model) {
  357. const items = new Map();
  358. for (const [id, obj] of this._decorationsMetadata) {
  359. if (items.has(obj.item)) {
  360. // an inlay item can be rendered as multiple decorations
  361. // but they will all uses the same range
  362. continue;
  363. }
  364. const range = model.getDecorationRange(id);
  365. if (range) {
  366. // update range with whatever the editor has tweaked it to
  367. const anchor = new InlayHintAnchor(range, obj.item.anchor.direction);
  368. const copy = obj.item.with({ anchor });
  369. items.set(obj.item, copy);
  370. }
  371. }
  372. return Array.from(items.values());
  373. }
  374. _getHintsRanges() {
  375. const extra = 30;
  376. const model = this._editor.getModel();
  377. const visibleRanges = this._editor.getVisibleRangesPlusViewportAboveBelow();
  378. const result = [];
  379. for (const range of visibleRanges.sort(Range.compareRangesUsingStarts)) {
  380. const extendedRange = model.validateRange(new Range(range.startLineNumber - extra, range.startColumn, range.endLineNumber + extra, range.endColumn));
  381. if (result.length === 0 || !Range.areIntersectingOrTouching(result[result.length - 1], extendedRange)) {
  382. result.push(extendedRange);
  383. }
  384. else {
  385. result[result.length - 1] = Range.plusRange(result[result.length - 1], extendedRange);
  386. }
  387. }
  388. return result;
  389. }
  390. _updateHintsDecorators(ranges, items) {
  391. var _a, _b;
  392. // utils to collect/create injected text decorations
  393. const newDecorationsData = [];
  394. const addInjectedText = (item, ref, content, cursorStops, attachedData) => {
  395. const opts = {
  396. content,
  397. inlineClassNameAffectsLetterSpacing: true,
  398. inlineClassName: ref.className,
  399. cursorStops,
  400. attachedData
  401. };
  402. newDecorationsData.push({
  403. item,
  404. classNameRef: ref,
  405. decoration: {
  406. range: item.anchor.range,
  407. options: {
  408. // className: "rangeHighlight", // DEBUG highlight to see to what range a hint is attached
  409. description: 'InlayHint',
  410. showIfCollapsed: item.anchor.range.isEmpty(),
  411. collapseOnReplaceEdit: !item.anchor.range.isEmpty(),
  412. stickiness: 0 /* TrackedRangeStickiness.AlwaysGrowsWhenTypingAtEdges */,
  413. [item.anchor.direction]: this._activeRenderMode === 0 /* RenderMode.Normal */ ? opts : undefined
  414. }
  415. }
  416. });
  417. };
  418. const addInjectedWhitespace = (item, isLast) => {
  419. const marginRule = this._ruleFactory.createClassNameRef({
  420. width: `${(fontSize / 3) | 0}px`,
  421. display: 'inline-block'
  422. });
  423. addInjectedText(item, marginRule, '\u200a', isLast ? InjectedTextCursorStops.Right : InjectedTextCursorStops.None);
  424. };
  425. //
  426. const { fontSize, fontFamily, padding, isUniform } = this._getLayoutInfo();
  427. const fontFamilyVar = '--code-editorInlayHintsFontFamily';
  428. this._editor.getContainerDomNode().style.setProperty(fontFamilyVar, fontFamily);
  429. for (const item of items) {
  430. // whitespace leading the actual label
  431. if (item.hint.paddingLeft) {
  432. addInjectedWhitespace(item, false);
  433. }
  434. // the label with its parts
  435. const parts = typeof item.hint.label === 'string'
  436. ? [{ label: item.hint.label }]
  437. : item.hint.label;
  438. for (let i = 0; i < parts.length; i++) {
  439. const part = parts[i];
  440. const isFirst = i === 0;
  441. const isLast = i === parts.length - 1;
  442. const cssProperties = {
  443. fontSize: `${fontSize}px`,
  444. fontFamily: `var(${fontFamilyVar}), ${EDITOR_FONT_DEFAULTS.fontFamily}`,
  445. verticalAlign: isUniform ? 'baseline' : 'middle',
  446. };
  447. if (isNonEmptyArray(item.hint.textEdits)) {
  448. cssProperties.cursor = 'default';
  449. }
  450. this._fillInColors(cssProperties, item.hint);
  451. if ((part.command || part.location) && ((_a = this._activeInlayHintPart) === null || _a === void 0 ? void 0 : _a.part.item) === item && this._activeInlayHintPart.part.index === i) {
  452. // active link!
  453. cssProperties.textDecoration = 'underline';
  454. if (this._activeInlayHintPart.hasTriggerModifier) {
  455. cssProperties.color = themeColorFromId(colors.editorActiveLinkForeground);
  456. cssProperties.cursor = 'pointer';
  457. }
  458. }
  459. if (padding) {
  460. if (isFirst && isLast) {
  461. // only element
  462. cssProperties.padding = `1px ${Math.max(1, fontSize / 4) | 0}px`;
  463. cssProperties.borderRadius = `${(fontSize / 4) | 0}px`;
  464. }
  465. else if (isFirst) {
  466. // first element
  467. cssProperties.padding = `1px 0 1px ${Math.max(1, fontSize / 4) | 0}px`;
  468. cssProperties.borderRadius = `${(fontSize / 4) | 0}px 0 0 ${(fontSize / 4) | 0}px`;
  469. }
  470. else if (isLast) {
  471. // last element
  472. cssProperties.padding = `1px ${Math.max(1, fontSize / 4) | 0}px 1px 0`;
  473. cssProperties.borderRadius = `0 ${(fontSize / 4) | 0}px ${(fontSize / 4) | 0}px 0`;
  474. }
  475. else {
  476. cssProperties.padding = `1px 0 1px 0`;
  477. }
  478. }
  479. addInjectedText(item, this._ruleFactory.createClassNameRef(cssProperties), fixSpace(part.label), isLast && !item.hint.paddingRight ? InjectedTextCursorStops.Right : InjectedTextCursorStops.None, new RenderedInlayHintLabelPart(item, i));
  480. }
  481. // whitespace trailing the actual label
  482. if (item.hint.paddingRight) {
  483. addInjectedWhitespace(item, true);
  484. }
  485. if (newDecorationsData.length > InlayHintsController._MAX_DECORATORS) {
  486. break;
  487. }
  488. }
  489. // collect all decoration ids that are affected by the ranges
  490. // and only update those decorations
  491. const decorationIdsToReplace = [];
  492. for (const range of ranges) {
  493. for (const { id } of (_b = this._editor.getDecorationsInRange(range)) !== null && _b !== void 0 ? _b : []) {
  494. const metadata = this._decorationsMetadata.get(id);
  495. if (metadata) {
  496. decorationIdsToReplace.push(id);
  497. metadata.classNameRef.dispose();
  498. this._decorationsMetadata.delete(id);
  499. }
  500. }
  501. }
  502. this._editor.changeDecorations(accessor => {
  503. const newDecorationIds = accessor.deltaDecorations(decorationIdsToReplace, newDecorationsData.map(d => d.decoration));
  504. for (let i = 0; i < newDecorationIds.length; i++) {
  505. const data = newDecorationsData[i];
  506. this._decorationsMetadata.set(newDecorationIds[i], data);
  507. }
  508. });
  509. }
  510. _fillInColors(props, hint) {
  511. if (hint.kind === languages.InlayHintKind.Parameter) {
  512. props.backgroundColor = themeColorFromId(colors.editorInlayHintParameterBackground);
  513. props.color = themeColorFromId(colors.editorInlayHintParameterForeground);
  514. }
  515. else if (hint.kind === languages.InlayHintKind.Type) {
  516. props.backgroundColor = themeColorFromId(colors.editorInlayHintTypeBackground);
  517. props.color = themeColorFromId(colors.editorInlayHintTypeForeground);
  518. }
  519. else {
  520. props.backgroundColor = themeColorFromId(colors.editorInlayHintBackground);
  521. props.color = themeColorFromId(colors.editorInlayHintForeground);
  522. }
  523. }
  524. _getLayoutInfo() {
  525. const options = this._editor.getOption(129 /* EditorOption.inlayHints */);
  526. const padding = options.padding;
  527. const editorFontSize = this._editor.getOption(48 /* EditorOption.fontSize */);
  528. const editorFontFamily = this._editor.getOption(45 /* EditorOption.fontFamily */);
  529. let fontSize = options.fontSize;
  530. if (!fontSize || fontSize < 5 || fontSize > editorFontSize) {
  531. fontSize = editorFontSize;
  532. }
  533. const fontFamily = options.fontFamily || editorFontFamily;
  534. const isUniform = !padding
  535. && fontFamily === editorFontFamily
  536. && fontSize === editorFontSize;
  537. return { fontSize, fontFamily, padding, isUniform };
  538. }
  539. _removeAllDecorations() {
  540. this._editor.removeDecorations(Array.from(this._decorationsMetadata.keys()));
  541. for (const obj of this._decorationsMetadata.values()) {
  542. obj.classNameRef.dispose();
  543. }
  544. this._decorationsMetadata.clear();
  545. }
  546. };
  547. InlayHintsController.ID = 'editor.contrib.InlayHints';
  548. InlayHintsController._MAX_DECORATORS = 1500;
  549. InlayHintsController = __decorate([
  550. __param(1, ILanguageFeaturesService),
  551. __param(2, ILanguageFeatureDebounceService),
  552. __param(3, IInlayHintsCache),
  553. __param(4, ICommandService),
  554. __param(5, INotificationService),
  555. __param(6, IInstantiationService)
  556. ], InlayHintsController);
  557. export { InlayHintsController };
  558. // Prevents the view from potentially visible whitespace
  559. function fixSpace(str) {
  560. const noBreakWhitespace = '\xa0';
  561. return str.replace(/[ \t]/g, noBreakWhitespace);
  562. }
  563. CommandsRegistry.registerCommand('_executeInlayHintProvider', (accessor, ...args) => __awaiter(void 0, void 0, void 0, function* () {
  564. const [uri, range] = args;
  565. assertType(URI.isUri(uri));
  566. assertType(Range.isIRange(range));
  567. const { inlayHintsProvider } = accessor.get(ILanguageFeaturesService);
  568. const ref = yield accessor.get(ITextModelService).createModelReference(uri);
  569. try {
  570. const model = yield InlayHintsFragments.create(inlayHintsProvider, ref.object.textEditorModel, [Range.lift(range)], CancellationToken.None);
  571. const result = model.items.map(i => i.hint);
  572. setTimeout(() => model.dispose(), 0); // dispose after sending to ext host
  573. return result;
  574. }
  575. finally {
  576. ref.dispose();
  577. }
  578. }));