Path: blob/main/src/vs/editor/contrib/suggest/browser/suggestAlternatives.ts
4797 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { IDisposable } from '../../../../base/common/lifecycle.js';6import { ICodeEditor } from '../../../browser/editorBrowser.js';7import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';8import { CompletionModel } from './completionModel.js';9import { ISelectedSuggestion } from './suggestWidget.js';1011export class SuggestAlternatives {1213static readonly OtherSuggestions = new RawContextKey<boolean>('hasOtherSuggestions', false);1415private readonly _ckOtherSuggestions: IContextKey<boolean>;1617private _index: number = 0;18private _model: CompletionModel | undefined;19private _acceptNext: ((selected: ISelectedSuggestion) => unknown) | undefined;20private _listener: IDisposable | undefined;21private _ignore: boolean | undefined;2223constructor(24private readonly _editor: ICodeEditor,25@IContextKeyService contextKeyService: IContextKeyService26) {27this._ckOtherSuggestions = SuggestAlternatives.OtherSuggestions.bindTo(contextKeyService);28}2930dispose(): void {31this.reset();32}3334reset(): void {35this._ckOtherSuggestions.reset();36this._listener?.dispose();37this._model = undefined;38this._acceptNext = undefined;39this._ignore = false;40}4142set({ model, index }: ISelectedSuggestion, acceptNext: (selected: ISelectedSuggestion) => unknown): void {4344// no suggestions -> nothing to do45if (model.items.length === 0) {46this.reset();47return;48}4950// no alternative suggestions -> nothing to do51const nextIndex = SuggestAlternatives._moveIndex(true, model, index);52if (nextIndex === index) {53this.reset();54return;55}5657this._acceptNext = acceptNext;58this._model = model;59this._index = index;60this._listener = this._editor.onDidChangeCursorPosition(() => {61if (!this._ignore) {62this.reset();63}64});65this._ckOtherSuggestions.set(true);66}6768private static _moveIndex(fwd: boolean, model: CompletionModel, index: number): number {69let newIndex = index;70for (let rounds = model.items.length; rounds > 0; rounds--) {71newIndex = (newIndex + model.items.length + (fwd ? +1 : -1)) % model.items.length;72if (newIndex === index) {73break;74}75if (!model.items[newIndex].completion.additionalTextEdits) {76break;77}78}79return newIndex;80}8182next(): void {83this._move(true);84}8586prev(): void {87this._move(false);88}8990private _move(fwd: boolean): void {91if (!this._model) {92// nothing to reason about93return;94}95try {96this._ignore = true;97this._index = SuggestAlternatives._moveIndex(fwd, this._model, this._index);98this._acceptNext!({ index: this._index, item: this._model.items[this._index], model: this._model });99} finally {100this._ignore = false;101}102}103}104105106