Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/suggest/browser/suggestAlternatives.ts
4797 views
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
6
import { IDisposable } from '../../../../base/common/lifecycle.js';
7
import { ICodeEditor } from '../../../browser/editorBrowser.js';
8
import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js';
9
import { CompletionModel } from './completionModel.js';
10
import { ISelectedSuggestion } from './suggestWidget.js';
11
12
export class SuggestAlternatives {
13
14
static readonly OtherSuggestions = new RawContextKey<boolean>('hasOtherSuggestions', false);
15
16
private readonly _ckOtherSuggestions: IContextKey<boolean>;
17
18
private _index: number = 0;
19
private _model: CompletionModel | undefined;
20
private _acceptNext: ((selected: ISelectedSuggestion) => unknown) | undefined;
21
private _listener: IDisposable | undefined;
22
private _ignore: boolean | undefined;
23
24
constructor(
25
private readonly _editor: ICodeEditor,
26
@IContextKeyService contextKeyService: IContextKeyService
27
) {
28
this._ckOtherSuggestions = SuggestAlternatives.OtherSuggestions.bindTo(contextKeyService);
29
}
30
31
dispose(): void {
32
this.reset();
33
}
34
35
reset(): void {
36
this._ckOtherSuggestions.reset();
37
this._listener?.dispose();
38
this._model = undefined;
39
this._acceptNext = undefined;
40
this._ignore = false;
41
}
42
43
set({ model, index }: ISelectedSuggestion, acceptNext: (selected: ISelectedSuggestion) => unknown): void {
44
45
// no suggestions -> nothing to do
46
if (model.items.length === 0) {
47
this.reset();
48
return;
49
}
50
51
// no alternative suggestions -> nothing to do
52
const nextIndex = SuggestAlternatives._moveIndex(true, model, index);
53
if (nextIndex === index) {
54
this.reset();
55
return;
56
}
57
58
this._acceptNext = acceptNext;
59
this._model = model;
60
this._index = index;
61
this._listener = this._editor.onDidChangeCursorPosition(() => {
62
if (!this._ignore) {
63
this.reset();
64
}
65
});
66
this._ckOtherSuggestions.set(true);
67
}
68
69
private static _moveIndex(fwd: boolean, model: CompletionModel, index: number): number {
70
let newIndex = index;
71
for (let rounds = model.items.length; rounds > 0; rounds--) {
72
newIndex = (newIndex + model.items.length + (fwd ? +1 : -1)) % model.items.length;
73
if (newIndex === index) {
74
break;
75
}
76
if (!model.items[newIndex].completion.additionalTextEdits) {
77
break;
78
}
79
}
80
return newIndex;
81
}
82
83
next(): void {
84
this._move(true);
85
}
86
87
prev(): void {
88
this._move(false);
89
}
90
91
private _move(fwd: boolean): void {
92
if (!this._model) {
93
// nothing to reason about
94
return;
95
}
96
try {
97
this._ignore = true;
98
this._index = SuggestAlternatives._moveIndex(fwd, this._model, this._index);
99
this._acceptNext!({ index: this._index, item: this._model.items[this._index], model: this._model });
100
} finally {
101
this._ignore = false;
102
}
103
}
104
}
105
106