415875cee1c16ea95799e5970644747f5d665117c98c4196f4904cd697237001106665e1bd7e2bb267b0ee16582b2192ef2d845b62316e807fcdfed8bb0cee 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  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 { createCancelablePromise, Delayer } from '../../../../base/common/async.js';
  15. import { onUnexpectedError } from '../../../../base/common/errors.js';
  16. import { Emitter } from '../../../../base/common/event.js';
  17. import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js';
  18. import { CharacterSet } from '../../../common/core/characterClassifier.js';
  19. import * as languages from '../../../common/languages.js';
  20. import { provideSignatureHelp } from './provideSignatureHelp.js';
  21. var ParameterHintState;
  22. (function (ParameterHintState) {
  23. ParameterHintState.Default = { type: 0 /* Type.Default */ };
  24. class Pending {
  25. constructor(request, previouslyActiveHints) {
  26. this.request = request;
  27. this.previouslyActiveHints = previouslyActiveHints;
  28. this.type = 2 /* Type.Pending */;
  29. }
  30. }
  31. ParameterHintState.Pending = Pending;
  32. class Active {
  33. constructor(hints) {
  34. this.hints = hints;
  35. this.type = 1 /* Type.Active */;
  36. }
  37. }
  38. ParameterHintState.Active = Active;
  39. })(ParameterHintState || (ParameterHintState = {}));
  40. export class ParameterHintsModel extends Disposable {
  41. constructor(editor, providers, delay = ParameterHintsModel.DEFAULT_DELAY) {
  42. super();
  43. this._onChangedHints = this._register(new Emitter());
  44. this.onChangedHints = this._onChangedHints.event;
  45. this.triggerOnType = false;
  46. this._state = ParameterHintState.Default;
  47. this._pendingTriggers = [];
  48. this._lastSignatureHelpResult = this._register(new MutableDisposable());
  49. this.triggerChars = new CharacterSet();
  50. this.retriggerChars = new CharacterSet();
  51. this.triggerId = 0;
  52. this.editor = editor;
  53. this.providers = providers;
  54. this.throttledDelayer = new Delayer(delay);
  55. this._register(this.editor.onDidBlurEditorWidget(() => this.cancel()));
  56. this._register(this.editor.onDidChangeConfiguration(() => this.onEditorConfigurationChange()));
  57. this._register(this.editor.onDidChangeModel(e => this.onModelChanged()));
  58. this._register(this.editor.onDidChangeModelLanguage(_ => this.onModelChanged()));
  59. this._register(this.editor.onDidChangeCursorSelection(e => this.onCursorChange(e)));
  60. this._register(this.editor.onDidChangeModelContent(e => this.onModelContentChange()));
  61. this._register(this.providers.onDidChange(this.onModelChanged, this));
  62. this._register(this.editor.onDidType(text => this.onDidType(text)));
  63. this.onEditorConfigurationChange();
  64. this.onModelChanged();
  65. }
  66. get state() { return this._state; }
  67. set state(value) {
  68. if (this._state.type === 2 /* ParameterHintState.Type.Pending */) {
  69. this._state.request.cancel();
  70. }
  71. this._state = value;
  72. }
  73. cancel(silent = false) {
  74. this.state = ParameterHintState.Default;
  75. this.throttledDelayer.cancel();
  76. if (!silent) {
  77. this._onChangedHints.fire(undefined);
  78. }
  79. }
  80. trigger(context, delay) {
  81. const model = this.editor.getModel();
  82. if (!model || !this.providers.has(model)) {
  83. return;
  84. }
  85. const triggerId = ++this.triggerId;
  86. this._pendingTriggers.push(context);
  87. this.throttledDelayer.trigger(() => {
  88. return this.doTrigger(triggerId);
  89. }, delay)
  90. .catch(onUnexpectedError);
  91. }
  92. next() {
  93. if (this.state.type !== 1 /* ParameterHintState.Type.Active */) {
  94. return;
  95. }
  96. const length = this.state.hints.signatures.length;
  97. const activeSignature = this.state.hints.activeSignature;
  98. const last = (activeSignature % length) === (length - 1);
  99. const cycle = this.editor.getOption(78 /* EditorOption.parameterHints */).cycle;
  100. // If there is only one signature, or we're on last signature of list
  101. if ((length < 2 || last) && !cycle) {
  102. this.cancel();
  103. return;
  104. }
  105. this.updateActiveSignature(last && cycle ? 0 : activeSignature + 1);
  106. }
  107. previous() {
  108. if (this.state.type !== 1 /* ParameterHintState.Type.Active */) {
  109. return;
  110. }
  111. const length = this.state.hints.signatures.length;
  112. const activeSignature = this.state.hints.activeSignature;
  113. const first = activeSignature === 0;
  114. const cycle = this.editor.getOption(78 /* EditorOption.parameterHints */).cycle;
  115. // If there is only one signature, or we're on first signature of list
  116. if ((length < 2 || first) && !cycle) {
  117. this.cancel();
  118. return;
  119. }
  120. this.updateActiveSignature(first && cycle ? length - 1 : activeSignature - 1);
  121. }
  122. updateActiveSignature(activeSignature) {
  123. if (this.state.type !== 1 /* ParameterHintState.Type.Active */) {
  124. return;
  125. }
  126. this.state = new ParameterHintState.Active(Object.assign(Object.assign({}, this.state.hints), { activeSignature }));
  127. this._onChangedHints.fire(this.state.hints);
  128. }
  129. doTrigger(triggerId) {
  130. return __awaiter(this, void 0, void 0, function* () {
  131. const isRetrigger = this.state.type === 1 /* ParameterHintState.Type.Active */ || this.state.type === 2 /* ParameterHintState.Type.Pending */;
  132. const activeSignatureHelp = this.getLastActiveHints();
  133. this.cancel(true);
  134. if (this._pendingTriggers.length === 0) {
  135. return false;
  136. }
  137. const context = this._pendingTriggers.reduce(mergeTriggerContexts);
  138. this._pendingTriggers = [];
  139. const triggerContext = {
  140. triggerKind: context.triggerKind,
  141. triggerCharacter: context.triggerCharacter,
  142. isRetrigger: isRetrigger,
  143. activeSignatureHelp: activeSignatureHelp
  144. };
  145. if (!this.editor.hasModel()) {
  146. return false;
  147. }
  148. const model = this.editor.getModel();
  149. const position = this.editor.getPosition();
  150. this.state = new ParameterHintState.Pending(createCancelablePromise(token => provideSignatureHelp(this.providers, model, position, triggerContext, token)), activeSignatureHelp);
  151. try {
  152. const result = yield this.state.request;
  153. // Check that we are still resolving the correct signature help
  154. if (triggerId !== this.triggerId) {
  155. result === null || result === void 0 ? void 0 : result.dispose();
  156. return false;
  157. }
  158. if (!result || !result.value.signatures || result.value.signatures.length === 0) {
  159. result === null || result === void 0 ? void 0 : result.dispose();
  160. this._lastSignatureHelpResult.clear();
  161. this.cancel();
  162. return false;
  163. }
  164. else {
  165. this.state = new ParameterHintState.Active(result.value);
  166. this._lastSignatureHelpResult.value = result;
  167. this._onChangedHints.fire(this.state.hints);
  168. return true;
  169. }
  170. }
  171. catch (error) {
  172. if (triggerId === this.triggerId) {
  173. this.state = ParameterHintState.Default;
  174. }
  175. onUnexpectedError(error);
  176. return false;
  177. }
  178. });
  179. }
  180. getLastActiveHints() {
  181. switch (this.state.type) {
  182. case 1 /* ParameterHintState.Type.Active */: return this.state.hints;
  183. case 2 /* ParameterHintState.Type.Pending */: return this.state.previouslyActiveHints;
  184. default: return undefined;
  185. }
  186. }
  187. get isTriggered() {
  188. return this.state.type === 1 /* ParameterHintState.Type.Active */
  189. || this.state.type === 2 /* ParameterHintState.Type.Pending */
  190. || this.throttledDelayer.isTriggered();
  191. }
  192. onModelChanged() {
  193. this.cancel();
  194. // Update trigger characters
  195. this.triggerChars = new CharacterSet();
  196. this.retriggerChars = new CharacterSet();
  197. const model = this.editor.getModel();
  198. if (!model) {
  199. return;
  200. }
  201. for (const support of this.providers.ordered(model)) {
  202. for (const ch of support.signatureHelpTriggerCharacters || []) {
  203. this.triggerChars.add(ch.charCodeAt(0));
  204. // All trigger characters are also considered retrigger characters
  205. this.retriggerChars.add(ch.charCodeAt(0));
  206. }
  207. for (const ch of support.signatureHelpRetriggerCharacters || []) {
  208. this.retriggerChars.add(ch.charCodeAt(0));
  209. }
  210. }
  211. }
  212. onDidType(text) {
  213. if (!this.triggerOnType) {
  214. return;
  215. }
  216. const lastCharIndex = text.length - 1;
  217. const triggerCharCode = text.charCodeAt(lastCharIndex);
  218. if (this.triggerChars.has(triggerCharCode) || this.isTriggered && this.retriggerChars.has(triggerCharCode)) {
  219. this.trigger({
  220. triggerKind: languages.SignatureHelpTriggerKind.TriggerCharacter,
  221. triggerCharacter: text.charAt(lastCharIndex),
  222. });
  223. }
  224. }
  225. onCursorChange(e) {
  226. if (e.source === 'mouse') {
  227. this.cancel();
  228. }
  229. else if (this.isTriggered) {
  230. this.trigger({ triggerKind: languages.SignatureHelpTriggerKind.ContentChange });
  231. }
  232. }
  233. onModelContentChange() {
  234. if (this.isTriggered) {
  235. this.trigger({ triggerKind: languages.SignatureHelpTriggerKind.ContentChange });
  236. }
  237. }
  238. onEditorConfigurationChange() {
  239. this.triggerOnType = this.editor.getOption(78 /* EditorOption.parameterHints */).enabled;
  240. if (!this.triggerOnType) {
  241. this.cancel();
  242. }
  243. }
  244. dispose() {
  245. this.cancel(true);
  246. super.dispose();
  247. }
  248. }
  249. ParameterHintsModel.DEFAULT_DELAY = 120; // ms
  250. function mergeTriggerContexts(previous, current) {
  251. switch (current.triggerKind) {
  252. case languages.SignatureHelpTriggerKind.Invoke:
  253. // Invoke overrides previous triggers.
  254. return current;
  255. case languages.SignatureHelpTriggerKind.ContentChange:
  256. // Ignore content changes triggers
  257. return previous;
  258. case languages.SignatureHelpTriggerKind.TriggerCharacter:
  259. default:
  260. return current;
  261. }
  262. }