3fdfcf767a05b5ef0ec33eea22f4a8727c7c4e1eb292ed958222cc12d6ae873a62ecfa2b45847bdfe040ee1b7cdf65c0d39d017d7f1d46adfbc993ebf3996d 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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 { CancellationToken } from '../../../../base/common/cancellation.js';
  24. import { FuzzyScore } from '../../../../base/common/filters.js';
  25. import { Iterable } from '../../../../base/common/iterator.js';
  26. import { RefCountedDisposable } from '../../../../base/common/lifecycle.js';
  27. import { registerEditorContribution } from '../../../browser/editorExtensions.js';
  28. import { ICodeEditorService } from '../../../browser/services/codeEditorService.js';
  29. import { Range } from '../../../common/core/range.js';
  30. import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
  31. import { CompletionItemInsertTextRule } from '../../../common/standalone/standaloneEnums.js';
  32. import { CompletionModel, LineContext } from './completionModel.js';
  33. import { CompletionOptions, provideSuggestionItems, QuickSuggestionsOptions } from './suggest.js';
  34. import { ISuggestMemoryService } from './suggestMemory.js';
  35. import { WordDistance } from './wordDistance.js';
  36. import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js';
  37. import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
  38. class SuggestInlineCompletion {
  39. constructor(range, insertText, filterText, additionalTextEdits, command, completion) {
  40. this.range = range;
  41. this.insertText = insertText;
  42. this.filterText = filterText;
  43. this.additionalTextEdits = additionalTextEdits;
  44. this.command = command;
  45. this.completion = completion;
  46. }
  47. }
  48. let InlineCompletionResults = class InlineCompletionResults extends RefCountedDisposable {
  49. constructor(model, line, word, completionModel, completions, _suggestMemoryService) {
  50. super(completions.disposable);
  51. this.model = model;
  52. this.line = line;
  53. this.word = word;
  54. this.completionModel = completionModel;
  55. this._suggestMemoryService = _suggestMemoryService;
  56. }
  57. canBeReused(model, line, word) {
  58. return this.model === model // same model
  59. && this.line === line
  60. && this.word.word.length > 0
  61. && this.word.startColumn === word.startColumn && this.word.endColumn < word.endColumn // same word
  62. && this.completionModel.incomplete.size === 0; // no incomplete results
  63. }
  64. get items() {
  65. var _a;
  66. const result = [];
  67. // Split items by preselected index. This ensures the memory-selected item shows first and that better/worst
  68. // ranked items are before/after
  69. const { items } = this.completionModel;
  70. const selectedIndex = this._suggestMemoryService.select(this.model, { lineNumber: this.line, column: this.word.endColumn + this.completionModel.lineContext.characterCountDelta }, items);
  71. const first = Iterable.slice(items, selectedIndex);
  72. const second = Iterable.slice(items, 0, selectedIndex);
  73. let resolveCount = 5;
  74. for (const item of Iterable.concat(first, second)) {
  75. if (item.score === FuzzyScore.Default) {
  76. // skip items that have no overlap
  77. continue;
  78. }
  79. const range = new Range(item.editStart.lineNumber, item.editStart.column, item.editInsertEnd.lineNumber, item.editInsertEnd.column + this.completionModel.lineContext.characterCountDelta // end PLUS character delta
  80. );
  81. const insertText = item.completion.insertTextRules && (item.completion.insertTextRules & CompletionItemInsertTextRule.InsertAsSnippet)
  82. ? { snippet: item.completion.insertText }
  83. : item.completion.insertText;
  84. result.push(new SuggestInlineCompletion(range, insertText, (_a = item.filterTextLow) !== null && _a !== void 0 ? _a : item.labelLow, item.completion.additionalTextEdits, item.completion.command, item));
  85. // resolve the first N suggestions eagerly
  86. if (resolveCount-- >= 0) {
  87. item.resolve(CancellationToken.None);
  88. }
  89. }
  90. return result;
  91. }
  92. };
  93. InlineCompletionResults = __decorate([
  94. __param(5, ISuggestMemoryService)
  95. ], InlineCompletionResults);
  96. let SuggestInlineCompletions = class SuggestInlineCompletions {
  97. constructor(_getEditorOption, _languageFeatureService, _clipboardService, _suggestMemoryService) {
  98. this._getEditorOption = _getEditorOption;
  99. this._languageFeatureService = _languageFeatureService;
  100. this._clipboardService = _clipboardService;
  101. this._suggestMemoryService = _suggestMemoryService;
  102. }
  103. provideInlineCompletions(model, position, context, token) {
  104. var _a;
  105. return __awaiter(this, void 0, void 0, function* () {
  106. if (context.selectedSuggestionInfo) {
  107. return;
  108. }
  109. const config = this._getEditorOption(81 /* EditorOption.quickSuggestions */, model);
  110. if (QuickSuggestionsOptions.isAllOff(config)) {
  111. // quick suggest is off (for this model/language)
  112. return;
  113. }
  114. model.tokenization.tokenizeIfCheap(position.lineNumber);
  115. const lineTokens = model.tokenization.getLineTokens(position.lineNumber);
  116. const tokenType = lineTokens.getStandardTokenType(lineTokens.findTokenIndexAtOffset(Math.max(position.column - 1 - 1, 0)));
  117. if (QuickSuggestionsOptions.valueFor(config, tokenType) !== 'inline') {
  118. // quick suggest is off (for this token)
  119. return undefined;
  120. }
  121. // We consider non-empty leading words and trigger characters. The latter only
  122. // when no word is being typed (word characters superseed trigger characters)
  123. let wordInfo = model.getWordAtPosition(position);
  124. let triggerCharacterInfo;
  125. if (!(wordInfo === null || wordInfo === void 0 ? void 0 : wordInfo.word)) {
  126. triggerCharacterInfo = this._getTriggerCharacterInfo(model, position);
  127. }
  128. if (!(wordInfo === null || wordInfo === void 0 ? void 0 : wordInfo.word) && !triggerCharacterInfo) {
  129. // not at word, not a trigger character
  130. return;
  131. }
  132. // ensure that we have word information and that we are at the end of a word
  133. // otherwise we stop because we don't want to do quick suggestions inside words
  134. if (!wordInfo) {
  135. wordInfo = model.getWordUntilPosition(position);
  136. }
  137. if (wordInfo.endColumn !== position.column) {
  138. return;
  139. }
  140. let result;
  141. const leadingLineContents = model.getValueInRange(new Range(position.lineNumber, 1, position.lineNumber, position.column));
  142. if (!triggerCharacterInfo && ((_a = this._lastResult) === null || _a === void 0 ? void 0 : _a.canBeReused(model, position.lineNumber, wordInfo))) {
  143. // reuse a previous result iff possible, only a refilter is needed
  144. // TODO@jrieken this can be improved further and only incomplete results can be updated
  145. // console.log(`REUSE with ${wordInfo.word}`);
  146. const newLineContext = new LineContext(leadingLineContents, position.column - this._lastResult.word.endColumn);
  147. this._lastResult.completionModel.lineContext = newLineContext;
  148. this._lastResult.acquire();
  149. result = this._lastResult;
  150. }
  151. else {
  152. // refesh model is required
  153. const completions = yield provideSuggestionItems(this._languageFeatureService.completionProvider, model, position, new CompletionOptions(undefined, undefined, triggerCharacterInfo === null || triggerCharacterInfo === void 0 ? void 0 : triggerCharacterInfo.providers), triggerCharacterInfo && { triggerKind: 1 /* CompletionTriggerKind.TriggerCharacter */, triggerCharacter: triggerCharacterInfo.ch }, token);
  154. let clipboardText;
  155. if (completions.needsClipboard) {
  156. clipboardText = yield this._clipboardService.readText();
  157. }
  158. const completionModel = new CompletionModel(completions.items, position.column, new LineContext(leadingLineContents, 0), WordDistance.None, this._getEditorOption(108 /* EditorOption.suggest */, model), this._getEditorOption(103 /* EditorOption.snippetSuggestions */, model), { boostFullMatch: false, firstMatchCanBeWeak: false }, clipboardText);
  159. result = new InlineCompletionResults(model, position.lineNumber, wordInfo, completionModel, completions, this._suggestMemoryService);
  160. }
  161. this._lastResult = result;
  162. return result;
  163. });
  164. }
  165. handleItemDidShow(_completions, item) {
  166. item.completion.resolve(CancellationToken.None);
  167. }
  168. freeInlineCompletions(result) {
  169. result.release();
  170. }
  171. _getTriggerCharacterInfo(model, position) {
  172. var _a;
  173. const ch = model.getValueInRange(Range.fromPositions({ lineNumber: position.lineNumber, column: position.column - 1 }, position));
  174. const providers = new Set();
  175. for (const provider of this._languageFeatureService.completionProvider.all(model)) {
  176. if ((_a = provider.triggerCharacters) === null || _a === void 0 ? void 0 : _a.includes(ch)) {
  177. providers.add(provider);
  178. }
  179. }
  180. if (providers.size === 0) {
  181. return undefined;
  182. }
  183. return { providers, ch };
  184. }
  185. };
  186. SuggestInlineCompletions = __decorate([
  187. __param(1, ILanguageFeaturesService),
  188. __param(2, IClipboardService),
  189. __param(3, ISuggestMemoryService)
  190. ], SuggestInlineCompletions);
  191. export { SuggestInlineCompletions };
  192. let EditorContribution = class EditorContribution {
  193. constructor(_editor, languageFeatureService, editorService, instaService) {
  194. // HACK - way to contribute something only once
  195. if (++EditorContribution._counter === 1) {
  196. const provider = instaService.createInstance(SuggestInlineCompletions, (id, model) => {
  197. var _a;
  198. // HACK - reuse the editor options world outside from a "normal" contribution
  199. const editor = (_a = editorService.listCodeEditors().find(editor => editor.getModel() === model)) !== null && _a !== void 0 ? _a : _editor;
  200. return editor.getOption(id);
  201. });
  202. EditorContribution._disposable = languageFeatureService.inlineCompletionsProvider.register('*', provider);
  203. }
  204. }
  205. dispose() {
  206. var _a;
  207. if (--EditorContribution._counter === 0) {
  208. (_a = EditorContribution._disposable) === null || _a === void 0 ? void 0 : _a.dispose();
  209. EditorContribution._disposable = undefined;
  210. }
  211. }
  212. };
  213. EditorContribution._counter = 0;
  214. EditorContribution = __decorate([
  215. __param(1, ILanguageFeaturesService),
  216. __param(2, ICodeEditorService),
  217. __param(3, IInstantiationService)
  218. ], EditorContribution);
  219. registerEditorContribution('suggest.inlineCompletionsProvider', EditorContribution);