| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653 |
- /*---------------------------------------------------------------------------------------------
- * Copyright (c) Microsoft Corporation. All rights reserved.
- * Licensed under the MIT License. See License.txt in the project root for license information.
- *--------------------------------------------------------------------------------------------*/
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
- 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;
- return c > 3 && r && Object.defineProperty(target, key, r), r;
- };
- var __param = (this && this.__param) || function (paramIndex, decorator) {
- return function (target, key) { decorator(target, key, paramIndex); }
- };
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
- return new (P || (P = Promise))(function (resolve, reject) {
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
- step((generator = generator.apply(thisArg, _arguments || [])).next());
- });
- };
- import { createCancelablePromise, RunOnceScheduler } from '../../../../base/common/async.js';
- import { CancellationToken } from '../../../../base/common/cancellation.js';
- import { onUnexpectedError, onUnexpectedExternalError } from '../../../../base/common/errors.js';
- import { Emitter } from '../../../../base/common/event.js';
- import { Disposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
- import { CoreEditingCommands } from '../../../browser/coreCommands.js';
- import { EditOperation } from '../../../common/core/editOperation.js';
- import { Range } from '../../../common/core/range.js';
- import { InlineCompletionTriggerKind } from '../../../common/languages.js';
- import { BaseGhostTextWidgetModel, GhostTextReplacement } from './ghostText.js';
- import { ICommandService } from '../../../../platform/commands/common/commands.js';
- import { inlineSuggestCommitId } from './consts.js';
- import { inlineCompletionToGhostText } from './inlineCompletionToGhostText.js';
- import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js';
- import { fixBracketsInLine } from '../../../common/model/bracketPairsTextModelPart/fixBrackets.js';
- import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js';
- import { ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js';
- import { SnippetParser } from '../../snippet/browser/snippetParser.js';
- import { SnippetController2 } from '../../snippet/browser/snippetController2.js';
- import { assertNever } from '../../../../base/common/types.js';
- import { matchesSubString } from '../../../../base/common/filters.js';
- import { getReadonlyEmptyArray } from './utils.js';
- import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js';
- let InlineCompletionsModel = class InlineCompletionsModel extends Disposable {
- constructor(editor, cache, commandService, languageConfigurationService, languageFeaturesService, debounceService, configurationService) {
- super();
- this.editor = editor;
- this.cache = cache;
- this.commandService = commandService;
- this.languageConfigurationService = languageConfigurationService;
- this.languageFeaturesService = languageFeaturesService;
- this.debounceService = debounceService;
- this.onDidChangeEmitter = new Emitter();
- this.onDidChange = this.onDidChangeEmitter.event;
- this.completionSession = this._register(new MutableDisposable());
- this.active = false;
- this.disposed = false;
- this.debounceValue = this.debounceService.for(this.languageFeaturesService.inlineCompletionsProvider, 'InlineCompletionsDebounce', { min: 50, max: 50 });
- this._register(commandService.onDidExecuteCommand((e) => {
- // These commands don't trigger onDidType.
- const commands = new Set([
- CoreEditingCommands.Tab.id,
- CoreEditingCommands.DeleteLeft.id,
- CoreEditingCommands.DeleteRight.id,
- inlineSuggestCommitId,
- 'acceptSelectedSuggestion',
- ]);
- if (commands.has(e.commandId) && editor.hasTextFocus()) {
- this.handleUserInput();
- }
- }));
- this._register(this.editor.onDidType((e) => {
- this.handleUserInput();
- }));
- this._register(this.editor.onDidChangeCursorPosition((e) => {
- if (e.reason === 3 /* CursorChangeReason.Explicit */ ||
- this.session && !this.session.isValid) {
- this.hide();
- }
- }));
- this._register(toDisposable(() => {
- this.disposed = true;
- }));
- this._register(this.editor.onDidBlurEditorWidget(() => {
- // This is a hidden setting very useful for debugging
- if (configurationService.getValue('editor.inlineSuggest.hideOnBlur')) {
- return;
- }
- this.hide();
- }));
- }
- handleUserInput() {
- if (this.session && !this.session.isValid) {
- this.hide();
- }
- setTimeout(() => {
- if (this.disposed) {
- return;
- }
- // Wait for the cursor update that happens in the same iteration loop iteration
- this.startSessionIfTriggered();
- }, 0);
- }
- get session() {
- return this.completionSession.value;
- }
- get ghostText() {
- var _a;
- return (_a = this.session) === null || _a === void 0 ? void 0 : _a.ghostText;
- }
- get minReservedLineCount() {
- return this.session ? this.session.minReservedLineCount : 0;
- }
- setExpanded(expanded) {
- var _a;
- (_a = this.session) === null || _a === void 0 ? void 0 : _a.setExpanded(expanded);
- }
- setActive(active) {
- var _a;
- this.active = active;
- if (active) {
- (_a = this.session) === null || _a === void 0 ? void 0 : _a.scheduleAutomaticUpdate();
- }
- }
- startSessionIfTriggered() {
- const suggestOptions = this.editor.getOption(57 /* EditorOption.inlineSuggest */);
- if (!suggestOptions.enabled) {
- return;
- }
- if (this.session && this.session.isValid) {
- return;
- }
- this.trigger(InlineCompletionTriggerKind.Automatic);
- }
- trigger(triggerKind) {
- if (this.completionSession.value) {
- if (triggerKind === InlineCompletionTriggerKind.Explicit) {
- void this.completionSession.value.ensureUpdateWithExplicitContext();
- }
- return;
- }
- this.completionSession.value = new InlineCompletionsSession(this.editor, this.editor.getPosition(), () => this.active, this.commandService, this.cache, triggerKind, this.languageConfigurationService, this.languageFeaturesService.inlineCompletionsProvider, this.debounceValue);
- this.completionSession.value.takeOwnership(this.completionSession.value.onDidChange(() => {
- this.onDidChangeEmitter.fire();
- }));
- }
- hide() {
- this.completionSession.clear();
- this.onDidChangeEmitter.fire();
- }
- commitCurrentSuggestion() {
- var _a;
- // Don't dispose the session, so that after committing, more suggestions are shown.
- (_a = this.session) === null || _a === void 0 ? void 0 : _a.commitCurrentCompletion();
- }
- showNext() {
- var _a;
- (_a = this.session) === null || _a === void 0 ? void 0 : _a.showNextInlineCompletion();
- }
- showPrevious() {
- var _a;
- (_a = this.session) === null || _a === void 0 ? void 0 : _a.showPreviousInlineCompletion();
- }
- hasMultipleInlineCompletions() {
- var _a;
- return __awaiter(this, void 0, void 0, function* () {
- const result = yield ((_a = this.session) === null || _a === void 0 ? void 0 : _a.hasMultipleInlineCompletions());
- return result !== undefined ? result : false;
- });
- }
- };
- InlineCompletionsModel = __decorate([
- __param(2, ICommandService),
- __param(3, ILanguageConfigurationService),
- __param(4, ILanguageFeaturesService),
- __param(5, ILanguageFeatureDebounceService),
- __param(6, IConfigurationService)
- ], InlineCompletionsModel);
- export { InlineCompletionsModel };
- export class InlineCompletionsSession extends BaseGhostTextWidgetModel {
- constructor(editor, triggerPosition, shouldUpdate, commandService, cache, initialTriggerKind, languageConfigurationService, registry, debounce) {
- super(editor);
- this.triggerPosition = triggerPosition;
- this.shouldUpdate = shouldUpdate;
- this.commandService = commandService;
- this.cache = cache;
- this.initialTriggerKind = initialTriggerKind;
- this.languageConfigurationService = languageConfigurationService;
- this.registry = registry;
- this.debounce = debounce;
- this.minReservedLineCount = 0;
- this.updateOperation = this._register(new MutableDisposable());
- this.updateSoon = this._register(new RunOnceScheduler(() => {
- const triggerKind = this.initialTriggerKind;
- // All subsequent triggers are automatic.
- this.initialTriggerKind = InlineCompletionTriggerKind.Automatic;
- return this.update(triggerKind);
- }, 50));
- this.filteredCompletions = [];
- //#region Selection
- // We use a semantic id to track the selection even if the cache changes.
- this.currentlySelectedCompletionId = undefined;
- let lastCompletionItem = undefined;
- this._register(this.onDidChange(() => {
- var _a;
- const currentCompletion = this.currentCompletion;
- if (currentCompletion && currentCompletion.sourceInlineCompletion !== lastCompletionItem) {
- lastCompletionItem = currentCompletion.sourceInlineCompletion;
- const provider = currentCompletion.sourceProvider;
- (_a = provider.handleItemDidShow) === null || _a === void 0 ? void 0 : _a.call(provider, currentCompletion.sourceInlineCompletions, lastCompletionItem);
- }
- }));
- this._register(toDisposable(() => {
- this.cache.clear();
- }));
- this._register(this.editor.onDidChangeCursorPosition((e) => {
- var _a;
- if (e.reason === 3 /* CursorChangeReason.Explicit */) {
- return;
- }
- // Ghost text depends on the cursor position
- (_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.updateRanges();
- if (this.cache.value) {
- this.updateFilteredInlineCompletions();
- this.onDidChangeEmitter.fire();
- }
- }));
- this._register(this.editor.onDidChangeModelContent((e) => {
- var _a;
- // Call this in case `onDidChangeModelContent` calls us first.
- (_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.updateRanges();
- this.updateFilteredInlineCompletions();
- this.scheduleAutomaticUpdate();
- }));
- this._register(this.registry.onDidChange(() => {
- this.updateSoon.schedule(this.debounce.get(this.editor.getModel()));
- }));
- this.scheduleAutomaticUpdate();
- }
- updateFilteredInlineCompletions() {
- if (!this.cache.value) {
- this.filteredCompletions = [];
- return;
- }
- const model = this.editor.getModel();
- const cursorPosition = model.validatePosition(this.editor.getPosition());
- this.filteredCompletions = this.cache.value.completions.filter(c => {
- const originalValue = model.getValueInRange(c.synchronizedRange).toLowerCase();
- const filterText = c.inlineCompletion.filterText.toLowerCase();
- const indent = model.getLineIndentColumn(c.synchronizedRange.startLineNumber);
- const cursorPosIndex = Math.max(0, cursorPosition.column - c.synchronizedRange.startColumn);
- let filterTextBefore = filterText.substring(0, cursorPosIndex);
- let filterTextAfter = filterText.substring(cursorPosIndex);
- let originalValueBefore = originalValue.substring(0, cursorPosIndex);
- let originalValueAfter = originalValue.substring(cursorPosIndex);
- if (c.synchronizedRange.startColumn <= indent) {
- // Remove indentation
- originalValueBefore = originalValueBefore.trimStart();
- if (originalValueBefore.length === 0) {
- originalValueAfter = originalValueAfter.trimStart();
- }
- filterTextBefore = filterTextBefore.trimStart();
- if (filterTextBefore.length === 0) {
- filterTextAfter = filterTextAfter.trimStart();
- }
- }
- return filterTextBefore.startsWith(originalValueBefore)
- && matchesSubString(originalValueAfter, filterTextAfter);
- });
- }
- fixAndGetIndexOfCurrentSelection() {
- if (!this.currentlySelectedCompletionId || !this.cache.value) {
- return 0;
- }
- if (this.cache.value.completions.length === 0) {
- // don't reset the selection in this case
- return 0;
- }
- const idx = this.filteredCompletions.findIndex(v => v.semanticId === this.currentlySelectedCompletionId);
- if (idx === -1) {
- // Reset the selection so that the selection does not jump back when it appears again
- this.currentlySelectedCompletionId = undefined;
- return 0;
- }
- return idx;
- }
- get currentCachedCompletion() {
- if (!this.cache.value) {
- return undefined;
- }
- return this.filteredCompletions[this.fixAndGetIndexOfCurrentSelection()];
- }
- showNextInlineCompletion() {
- return __awaiter(this, void 0, void 0, function* () {
- yield this.ensureUpdateWithExplicitContext();
- const completions = this.filteredCompletions || [];
- if (completions.length > 0) {
- const newIdx = (this.fixAndGetIndexOfCurrentSelection() + 1) % completions.length;
- this.currentlySelectedCompletionId = completions[newIdx].semanticId;
- }
- else {
- this.currentlySelectedCompletionId = undefined;
- }
- this.onDidChangeEmitter.fire();
- });
- }
- showPreviousInlineCompletion() {
- return __awaiter(this, void 0, void 0, function* () {
- yield this.ensureUpdateWithExplicitContext();
- const completions = this.filteredCompletions || [];
- if (completions.length > 0) {
- const newIdx = (this.fixAndGetIndexOfCurrentSelection() + completions.length - 1) % completions.length;
- this.currentlySelectedCompletionId = completions[newIdx].semanticId;
- }
- else {
- this.currentlySelectedCompletionId = undefined;
- }
- this.onDidChangeEmitter.fire();
- });
- }
- ensureUpdateWithExplicitContext() {
- var _a;
- return __awaiter(this, void 0, void 0, function* () {
- if (this.updateOperation.value) {
- // Restart or wait for current update operation
- if (this.updateOperation.value.triggerKind === InlineCompletionTriggerKind.Explicit) {
- yield this.updateOperation.value.promise;
- }
- else {
- yield this.update(InlineCompletionTriggerKind.Explicit);
- }
- }
- else if (((_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.triggerKind) !== InlineCompletionTriggerKind.Explicit) {
- // Refresh cache
- yield this.update(InlineCompletionTriggerKind.Explicit);
- }
- });
- }
- hasMultipleInlineCompletions() {
- var _a;
- return __awaiter(this, void 0, void 0, function* () {
- yield this.ensureUpdateWithExplicitContext();
- return (((_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.completions.length) || 0) > 1;
- });
- }
- //#endregion
- get ghostText() {
- const currentCompletion = this.currentCompletion;
- if (!currentCompletion) {
- return undefined;
- }
- const cursorPosition = this.editor.getPosition();
- if (currentCompletion.range.getEndPosition().isBefore(cursorPosition)) {
- return undefined;
- }
- const mode = this.editor.getOptions().get(57 /* EditorOption.inlineSuggest */).mode;
- const ghostText = inlineCompletionToGhostText(currentCompletion, this.editor.getModel(), mode, cursorPosition);
- if (ghostText) {
- if (ghostText.isEmpty()) {
- return undefined;
- }
- return ghostText;
- }
- return new GhostTextReplacement(currentCompletion.range.startLineNumber, currentCompletion.range.startColumn, currentCompletion.range.endColumn - currentCompletion.range.startColumn, currentCompletion.insertText.split('\n'), 0);
- }
- get currentCompletion() {
- const completion = this.currentCachedCompletion;
- if (!completion) {
- return undefined;
- }
- return completion.toLiveInlineCompletion();
- }
- get isValid() {
- return this.editor.getPosition().lineNumber === this.triggerPosition.lineNumber;
- }
- scheduleAutomaticUpdate() {
- // Since updateSoon debounces, starvation can happen.
- // To prevent stale cache, we clear the current update operation.
- this.updateOperation.clear();
- this.updateSoon.schedule(this.debounce.get(this.editor.getModel()));
- }
- update(triggerKind) {
- return __awaiter(this, void 0, void 0, function* () {
- if (!this.shouldUpdate()) {
- return;
- }
- const position = this.editor.getPosition();
- const startTime = new Date();
- const promise = createCancelablePromise((token) => __awaiter(this, void 0, void 0, function* () {
- let result;
- try {
- result = yield provideInlineCompletions(this.registry, position, this.editor.getModel(), { triggerKind, selectedSuggestionInfo: undefined }, token, this.languageConfigurationService);
- const endTime = new Date();
- this.debounce.update(this.editor.getModel(), endTime.getTime() - startTime.getTime());
- }
- catch (e) {
- onUnexpectedError(e);
- return;
- }
- if (token.isCancellationRequested) {
- return;
- }
- this.cache.setValue(this.editor, result, triggerKind);
- this.updateFilteredInlineCompletions();
- this.onDidChangeEmitter.fire();
- }));
- const operation = new UpdateOperation(promise, triggerKind);
- this.updateOperation.value = operation;
- yield promise;
- if (this.updateOperation.value === operation) {
- this.updateOperation.clear();
- }
- });
- }
- takeOwnership(disposable) {
- this._register(disposable);
- }
- commitCurrentCompletion() {
- const ghostText = this.ghostText;
- if (!ghostText) {
- // No ghost text was shown for this completion.
- // Thus, we don't want to commit anything.
- return;
- }
- const completion = this.currentCompletion;
- if (completion) {
- this.commit(completion);
- }
- }
- commit(completion) {
- var _a;
- // Mark the cache as stale, but don't dispose it yet,
- // otherwise command args might get disposed.
- const cache = this.cache.clearAndLeak();
- if (completion.snippetInfo) {
- this.editor.executeEdits('inlineSuggestion.accept', [
- EditOperation.replaceMove(completion.range, ''),
- ...completion.additionalTextEdits
- ]);
- this.editor.setPosition(completion.snippetInfo.range.getStartPosition());
- (_a = SnippetController2.get(this.editor)) === null || _a === void 0 ? void 0 : _a.insert(completion.snippetInfo.snippet);
- }
- else {
- this.editor.executeEdits('inlineSuggestion.accept', [
- EditOperation.replaceMove(completion.range, completion.insertText),
- ...completion.additionalTextEdits
- ]);
- }
- if (completion.command) {
- this.commandService
- .executeCommand(completion.command.id, ...(completion.command.arguments || []))
- .finally(() => {
- cache === null || cache === void 0 ? void 0 : cache.dispose();
- })
- .then(undefined, onUnexpectedExternalError);
- }
- else {
- cache === null || cache === void 0 ? void 0 : cache.dispose();
- }
- this.onDidChangeEmitter.fire();
- }
- get commands() {
- var _a;
- const lists = new Set(((_a = this.cache.value) === null || _a === void 0 ? void 0 : _a.completions.map(c => c.inlineCompletion.sourceInlineCompletions)) || []);
- return [...lists].flatMap(l => l.commands || []);
- }
- }
- export class UpdateOperation {
- constructor(promise, triggerKind) {
- this.promise = promise;
- this.triggerKind = triggerKind;
- }
- dispose() {
- this.promise.cancel();
- }
- }
- /**
- * The cache keeps itself in sync with the editor.
- * It also owns the completions result and disposes it when the cache is diposed.
- */
- export class SynchronizedInlineCompletionsCache extends Disposable {
- constructor(completionsSource, editor, onChange, triggerKind) {
- super();
- this.editor = editor;
- this.onChange = onChange;
- this.triggerKind = triggerKind;
- this.isDisposing = false;
- const decorationIds = editor.changeDecorations((changeAccessor) => {
- return changeAccessor.deltaDecorations([], completionsSource.items.map(i => ({
- range: i.range,
- options: {
- description: 'inline-completion-tracking-range'
- },
- })));
- });
- this._register(toDisposable(() => {
- this.isDisposing = true;
- editor.removeDecorations(decorationIds);
- }));
- this.completions = completionsSource.items.map((c, idx) => new CachedInlineCompletion(c, decorationIds[idx]));
- this._register(editor.onDidChangeModelContent(() => {
- this.updateRanges();
- }));
- this._register(completionsSource);
- }
- updateRanges() {
- if (this.isDisposing) {
- return;
- }
- let hasChanged = false;
- const model = this.editor.getModel();
- for (const c of this.completions) {
- const newRange = model.getDecorationRange(c.decorationId);
- if (!newRange) {
- onUnexpectedError(new Error('Decoration has no range'));
- continue;
- }
- if (!c.synchronizedRange.equalsRange(newRange)) {
- hasChanged = true;
- c.synchronizedRange = newRange;
- }
- }
- if (hasChanged) {
- this.onChange();
- }
- }
- }
- class CachedInlineCompletion {
- constructor(inlineCompletion, decorationId) {
- this.inlineCompletion = inlineCompletion;
- this.decorationId = decorationId;
- this.semanticId = JSON.stringify({
- text: this.inlineCompletion.insertText,
- abbreviation: this.inlineCompletion.filterText,
- startLine: this.inlineCompletion.range.startLineNumber,
- startColumn: this.inlineCompletion.range.startColumn,
- command: this.inlineCompletion.command
- });
- this.synchronizedRange = inlineCompletion.range;
- }
- toLiveInlineCompletion() {
- return {
- insertText: this.inlineCompletion.insertText,
- range: this.synchronizedRange,
- command: this.inlineCompletion.command,
- sourceProvider: this.inlineCompletion.sourceProvider,
- sourceInlineCompletions: this.inlineCompletion.sourceInlineCompletions,
- sourceInlineCompletion: this.inlineCompletion.sourceInlineCompletion,
- snippetInfo: this.inlineCompletion.snippetInfo,
- filterText: this.inlineCompletion.filterText,
- additionalTextEdits: this.inlineCompletion.additionalTextEdits,
- };
- }
- }
- export function provideInlineCompletions(registry, position, model, context, token = CancellationToken.None, languageConfigurationService) {
- return __awaiter(this, void 0, void 0, function* () {
- const defaultReplaceRange = getDefaultRange(position, model);
- const providers = registry.all(model);
- const results = yield Promise.all(providers.map((provider) => __awaiter(this, void 0, void 0, function* () {
- const completions = yield Promise.resolve(provider.provideInlineCompletions(model, position, context, token)).catch(onUnexpectedExternalError);
- return ({
- completions,
- provider,
- dispose: () => {
- if (completions) {
- provider.freeInlineCompletions(completions);
- }
- }
- });
- })));
- const itemsByHash = new Map();
- for (const result of results) {
- const completions = result.completions;
- if (!completions) {
- continue;
- }
- for (const item of completions.items) {
- let range = item.range ? Range.lift(item.range) : defaultReplaceRange;
- if (range.startLineNumber !== range.endLineNumber) {
- // Ignore invalid ranges.
- continue;
- }
- let insertText;
- let snippetInfo;
- if (typeof item.insertText === 'string') {
- insertText = item.insertText;
- if (languageConfigurationService && item.completeBracketPairs) {
- insertText = closeBrackets(insertText, range.getStartPosition(), model, languageConfigurationService);
- // Modify range depending on if brackets are added or removed
- const diff = insertText.length - item.insertText.length;
- if (diff !== 0) {
- range = new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn + diff);
- }
- }
- snippetInfo = undefined;
- }
- else if ('snippet' in item.insertText) {
- const snippet = new SnippetParser().parse(item.insertText.snippet);
- insertText = snippet.toString();
- snippetInfo = {
- snippet: item.insertText.snippet,
- range: range
- };
- }
- else {
- assertNever(item.insertText);
- }
- const trackedItem = ({
- insertText,
- snippetInfo,
- range,
- command: item.command,
- sourceProvider: result.provider,
- sourceInlineCompletions: completions,
- sourceInlineCompletion: item,
- filterText: item.filterText || insertText,
- additionalTextEdits: item.additionalTextEdits || getReadonlyEmptyArray()
- });
- itemsByHash.set(JSON.stringify({ insertText, range: item.range }), trackedItem);
- }
- }
- return {
- items: [...itemsByHash.values()],
- dispose: () => {
- for (const result of results) {
- result.dispose();
- }
- },
- };
- });
- }
- function getDefaultRange(position, model) {
- const word = model.getWordAtPosition(position);
- const maxColumn = model.getLineMaxColumn(position.lineNumber);
- // By default, always replace up until the end of the current line.
- // This default might be subject to change!
- return word
- ? new Range(position.lineNumber, word.startColumn, position.lineNumber, maxColumn)
- : Range.fromPositions(position, position.with(undefined, maxColumn));
- }
- function closeBrackets(text, position, model, languageConfigurationService) {
- const lineStart = model.getLineContent(position.lineNumber).substring(0, position.column - 1);
- const newLine = lineStart + text;
- const newTokens = model.tokenization.tokenizeLineWithEdit(position, newLine.length - (position.column - 1), text);
- const slicedTokens = newTokens === null || newTokens === void 0 ? void 0 : newTokens.sliceAndInflate(position.column - 1, newLine.length, 0);
- if (!slicedTokens) {
- return text;
- }
- const newText = fixBracketsInLine(slicedTokens, languageConfigurationService);
- return newText;
- }
|