dab9f47c3745ecd861ed41c61362e903fb668dbcd0c4e82d2ea66221c5e2a437b722bbc79d31d75054a13541e835ee1680677036c52da8d01b414e17e5aa6b 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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 { createCancelablePromise, RunOnceScheduler } from '../../../../base/common/async.js';
  24. import { onUnexpectedError } from '../../../../base/common/errors.js';
  25. import { MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
  26. import { InlineCompletionTriggerKind } from '../../../common/languages.js';
  27. import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
  28. import { BaseGhostTextWidgetModel, GhostText } from './ghostText.js';
  29. import { provideInlineCompletions, UpdateOperation } from './inlineCompletionsModel.js';
  30. import { inlineCompletionToGhostText, minimizeInlineCompletion } from './inlineCompletionToGhostText.js';
  31. import { SuggestWidgetInlineCompletionProvider } from './suggestWidgetInlineCompletionProvider.js';
  32. let SuggestWidgetPreviewModel = class SuggestWidgetPreviewModel extends BaseGhostTextWidgetModel {
  33. constructor(editor, cache, languageFeaturesService) {
  34. super(editor);
  35. this.cache = cache;
  36. this.languageFeaturesService = languageFeaturesService;
  37. this.suggestionInlineCompletionSource = this._register(new SuggestWidgetInlineCompletionProvider(this.editor,
  38. // Use the first cache item (if any) as preselection.
  39. () => { var _a, _b; return (_b = (_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.completions[0]) === null || _b === void 0 ? void 0 : _b.toLiveInlineCompletion(); }));
  40. this.updateOperation = this._register(new MutableDisposable());
  41. this.updateCacheSoon = this._register(new RunOnceScheduler(() => this.updateCache(), 50));
  42. this.minReservedLineCount = 0;
  43. this._register(this.suggestionInlineCompletionSource.onDidChange(() => {
  44. if (!this.editor.hasModel()) {
  45. // onDidChange might be called when calling setModel on the editor, before we are disposed.
  46. return;
  47. }
  48. this.updateCacheSoon.schedule();
  49. const suggestWidgetState = this.suggestionInlineCompletionSource.state;
  50. if (!suggestWidgetState) {
  51. this.minReservedLineCount = 0;
  52. }
  53. const newGhostText = this.ghostText;
  54. if (newGhostText) {
  55. this.minReservedLineCount = Math.max(this.minReservedLineCount, sum(newGhostText.parts.map(p => p.lines.length - 1)));
  56. }
  57. if (this.minReservedLineCount >= 1) {
  58. this.suggestionInlineCompletionSource.forceRenderingAbove();
  59. }
  60. else {
  61. this.suggestionInlineCompletionSource.stopForceRenderingAbove();
  62. }
  63. this.onDidChangeEmitter.fire();
  64. }));
  65. this._register(this.cache.onDidChange(() => {
  66. this.onDidChangeEmitter.fire();
  67. }));
  68. this._register(this.editor.onDidChangeCursorPosition((e) => {
  69. this.minReservedLineCount = 0;
  70. this.updateCacheSoon.schedule();
  71. this.onDidChangeEmitter.fire();
  72. }));
  73. this._register(toDisposable(() => this.suggestionInlineCompletionSource.stopForceRenderingAbove()));
  74. }
  75. get isActive() {
  76. return this.suggestionInlineCompletionSource.state !== undefined;
  77. }
  78. isSuggestionPreviewEnabled() {
  79. const suggestOptions = this.editor.getOption(108 /* EditorOption.suggest */);
  80. return suggestOptions.preview;
  81. }
  82. updateCache() {
  83. return __awaiter(this, void 0, void 0, function* () {
  84. const state = this.suggestionInlineCompletionSource.state;
  85. if (!state || !state.selectedItem) {
  86. return;
  87. }
  88. const info = {
  89. text: state.selectedItem.normalizedInlineCompletion.insertText,
  90. range: state.selectedItem.normalizedInlineCompletion.range,
  91. isSnippetText: state.selectedItem.isSnippetText,
  92. completionKind: state.selectedItem.completionItemKind,
  93. };
  94. const position = this.editor.getPosition();
  95. if (state.selectedItem.isSnippetText ||
  96. state.selectedItem.completionItemKind === 27 /* CompletionItemKind.Snippet */ ||
  97. state.selectedItem.completionItemKind === 20 /* CompletionItemKind.File */ ||
  98. state.selectedItem.completionItemKind === 23 /* CompletionItemKind.Folder */) {
  99. // Don't ask providers for these types of suggestions.
  100. this.cache.clear();
  101. return;
  102. }
  103. const promise = createCancelablePromise((token) => __awaiter(this, void 0, void 0, function* () {
  104. let result;
  105. try {
  106. result = yield provideInlineCompletions(this.languageFeaturesService.inlineCompletionsProvider, position, this.editor.getModel(), { triggerKind: InlineCompletionTriggerKind.Automatic, selectedSuggestionInfo: info }, token);
  107. }
  108. catch (e) {
  109. onUnexpectedError(e);
  110. return;
  111. }
  112. if (token.isCancellationRequested) {
  113. result.dispose();
  114. return;
  115. }
  116. this.cache.setValue(this.editor, result, InlineCompletionTriggerKind.Automatic);
  117. this.onDidChangeEmitter.fire();
  118. }));
  119. const operation = new UpdateOperation(promise, InlineCompletionTriggerKind.Automatic);
  120. this.updateOperation.value = operation;
  121. yield promise;
  122. if (this.updateOperation.value === operation) {
  123. this.updateOperation.clear();
  124. }
  125. });
  126. }
  127. get ghostText() {
  128. var _a, _b, _c;
  129. const isSuggestionPreviewEnabled = this.isSuggestionPreviewEnabled();
  130. const model = this.editor.getModel();
  131. const augmentedCompletion = minimizeInlineCompletion(model, (_b = (_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.completions[0]) === null || _b === void 0 ? void 0 : _b.toLiveInlineCompletion());
  132. const suggestWidgetState = this.suggestionInlineCompletionSource.state;
  133. const suggestInlineCompletion = minimizeInlineCompletion(model, (_c = suggestWidgetState === null || suggestWidgetState === void 0 ? void 0 : suggestWidgetState.selectedItem) === null || _c === void 0 ? void 0 : _c.normalizedInlineCompletion);
  134. const isAugmentedCompletionValid = augmentedCompletion
  135. && suggestInlineCompletion
  136. && augmentedCompletion.insertText.startsWith(suggestInlineCompletion.insertText)
  137. && augmentedCompletion.range.equalsRange(suggestInlineCompletion.range);
  138. if (!isSuggestionPreviewEnabled && !isAugmentedCompletionValid) {
  139. return undefined;
  140. }
  141. // If the augmented completion is not valid and there is no suggest inline completion, we still show the augmented completion.
  142. const finalCompletion = isAugmentedCompletionValid ? augmentedCompletion : (suggestInlineCompletion || augmentedCompletion);
  143. const inlineCompletionPreviewLength = isAugmentedCompletionValid ? finalCompletion.insertText.length - suggestInlineCompletion.insertText.length : 0;
  144. const newGhostText = this.toGhostText(finalCompletion, inlineCompletionPreviewLength);
  145. return newGhostText;
  146. }
  147. toGhostText(completion, inlineCompletionPreviewLength) {
  148. const mode = this.editor.getOptions().get(108 /* EditorOption.suggest */).previewMode;
  149. return completion
  150. ? (inlineCompletionToGhostText(completion, this.editor.getModel(), mode, this.editor.getPosition(), inlineCompletionPreviewLength) ||
  151. // Show an invisible ghost text to reserve space
  152. new GhostText(completion.range.endLineNumber, [], this.minReservedLineCount))
  153. : undefined;
  154. }
  155. };
  156. SuggestWidgetPreviewModel = __decorate([
  157. __param(2, ILanguageFeaturesService)
  158. ], SuggestWidgetPreviewModel);
  159. export { SuggestWidgetPreviewModel };
  160. function sum(arr) {
  161. return arr.reduce((a, b) => a + b, 0);
  162. }