4e802c6f5da23472fb810ecb02a4c376bbef17ae60681cb5b9cfa30a0419d8d81e1523603795ada681a78b3c6774abaa0f6f56edfc83cbe4643451d77d0f23 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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 __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
  6. function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
  7. return new (P || (P = Promise))(function (resolve, reject) {
  8. function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
  9. function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
  10. function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
  11. step((generator = generator.apply(thisArg, _arguments || [])).next());
  12. });
  13. };
  14. import { CancellationError, onUnexpectedExternalError } from '../../../../base/common/errors.js';
  15. import { DisposableStore } from '../../../../base/common/lifecycle.js';
  16. import { Position } from '../../../common/core/position.js';
  17. import { Range } from '../../../common/core/range.js';
  18. import { Schemas } from '../../../../base/common/network.js';
  19. import { URI } from '../../../../base/common/uri.js';
  20. export class InlayHintAnchor {
  21. constructor(range, direction) {
  22. this.range = range;
  23. this.direction = direction;
  24. }
  25. }
  26. export class InlayHintItem {
  27. constructor(hint, anchor, provider) {
  28. this.hint = hint;
  29. this.anchor = anchor;
  30. this.provider = provider;
  31. this._isResolved = false;
  32. }
  33. with(delta) {
  34. const result = new InlayHintItem(this.hint, delta.anchor, this.provider);
  35. result._isResolved = this._isResolved;
  36. result._currentResolve = this._currentResolve;
  37. return result;
  38. }
  39. resolve(token) {
  40. return __awaiter(this, void 0, void 0, function* () {
  41. if (typeof this.provider.resolveInlayHint !== 'function') {
  42. return;
  43. }
  44. if (this._currentResolve) {
  45. // wait for an active resolve operation and try again
  46. // when that's done.
  47. yield this._currentResolve;
  48. if (token.isCancellationRequested) {
  49. return;
  50. }
  51. return this.resolve(token);
  52. }
  53. if (!this._isResolved) {
  54. this._currentResolve = this._doResolve(token)
  55. .finally(() => this._currentResolve = undefined);
  56. }
  57. yield this._currentResolve;
  58. });
  59. }
  60. _doResolve(token) {
  61. var _a, _b;
  62. return __awaiter(this, void 0, void 0, function* () {
  63. try {
  64. const newHint = yield Promise.resolve(this.provider.resolveInlayHint(this.hint, token));
  65. this.hint.tooltip = (_a = newHint === null || newHint === void 0 ? void 0 : newHint.tooltip) !== null && _a !== void 0 ? _a : this.hint.tooltip;
  66. this.hint.label = (_b = newHint === null || newHint === void 0 ? void 0 : newHint.label) !== null && _b !== void 0 ? _b : this.hint.label;
  67. this._isResolved = true;
  68. }
  69. catch (err) {
  70. onUnexpectedExternalError(err);
  71. this._isResolved = false;
  72. }
  73. });
  74. }
  75. }
  76. export class InlayHintsFragments {
  77. constructor(ranges, data, model) {
  78. this._disposables = new DisposableStore();
  79. this.ranges = ranges;
  80. this.provider = new Set();
  81. const items = [];
  82. for (const [list, provider] of data) {
  83. this._disposables.add(list);
  84. this.provider.add(provider);
  85. for (const hint of list.hints) {
  86. // compute the range to which the item should be attached to
  87. const position = model.validatePosition(hint.position);
  88. let direction = 'before';
  89. const wordRange = InlayHintsFragments._getRangeAtPosition(model, position);
  90. let range;
  91. if (wordRange.getStartPosition().isBefore(position)) {
  92. range = Range.fromPositions(wordRange.getStartPosition(), position);
  93. direction = 'after';
  94. }
  95. else {
  96. range = Range.fromPositions(position, wordRange.getEndPosition());
  97. direction = 'before';
  98. }
  99. items.push(new InlayHintItem(hint, new InlayHintAnchor(range, direction), provider));
  100. }
  101. }
  102. this.items = items.sort((a, b) => Position.compare(a.hint.position, b.hint.position));
  103. }
  104. static create(registry, model, ranges, token) {
  105. return __awaiter(this, void 0, void 0, function* () {
  106. const data = [];
  107. const promises = registry.ordered(model).reverse().map(provider => ranges.map((range) => __awaiter(this, void 0, void 0, function* () {
  108. try {
  109. const result = yield provider.provideInlayHints(model, range, token);
  110. if (result === null || result === void 0 ? void 0 : result.hints.length) {
  111. data.push([result, provider]);
  112. }
  113. }
  114. catch (err) {
  115. onUnexpectedExternalError(err);
  116. }
  117. })));
  118. yield Promise.all(promises.flat());
  119. if (token.isCancellationRequested || model.isDisposed()) {
  120. throw new CancellationError();
  121. }
  122. return new InlayHintsFragments(ranges, data, model);
  123. });
  124. }
  125. dispose() {
  126. this._disposables.dispose();
  127. }
  128. static _getRangeAtPosition(model, position) {
  129. const line = position.lineNumber;
  130. const word = model.getWordAtPosition(position);
  131. if (word) {
  132. // always prefer the word range
  133. return new Range(line, word.startColumn, line, word.endColumn);
  134. }
  135. model.tokenization.tokenizeIfCheap(line);
  136. const tokens = model.tokenization.getLineTokens(line);
  137. const offset = position.column - 1;
  138. const idx = tokens.findTokenIndexAtOffset(offset);
  139. let start = tokens.getStartOffset(idx);
  140. let end = tokens.getEndOffset(idx);
  141. if (end - start === 1) {
  142. // single character token, when at its end try leading/trailing token instead
  143. if (start === offset && idx > 1) {
  144. // leading token
  145. start = tokens.getStartOffset(idx - 1);
  146. end = tokens.getEndOffset(idx - 1);
  147. }
  148. else if (end === offset && idx < tokens.getCount() - 1) {
  149. // trailing token
  150. start = tokens.getStartOffset(idx + 1);
  151. end = tokens.getEndOffset(idx + 1);
  152. }
  153. }
  154. return new Range(line, start + 1, line, end + 1);
  155. }
  156. }
  157. export function asCommandLink(command) {
  158. return URI.from({
  159. scheme: Schemas.command,
  160. path: command.id,
  161. query: command.arguments && encodeURIComponent(JSON.stringify(command.arguments))
  162. }).toString();
  163. }