Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/suggest/browser/suggestController.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 { alert } from '../../../../base/browser/ui/aria/aria.js';
7
import { isNonEmptyArray } from '../../../../base/common/arrays.js';
8
import { CancellationTokenSource } from '../../../../base/common/cancellation.js';
9
import { onUnexpectedError, onUnexpectedExternalError } from '../../../../base/common/errors.js';
10
import { Emitter, Event } from '../../../../base/common/event.js';
11
import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js';
12
import { DisposableStore, dispose, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js';
13
import { StopWatch } from '../../../../base/common/stopwatch.js';
14
import { assertType, isObject } from '../../../../base/common/types.js';
15
import { StableEditorScrollState } from '../../../browser/stableEditorScroll.js';
16
import { ICodeEditor } from '../../../browser/editorBrowser.js';
17
import { EditorAction, EditorCommand, EditorContributionInstantiation, registerEditorAction, registerEditorCommand, registerEditorContribution, ServicesAccessor } from '../../../browser/editorExtensions.js';
18
import { EditorOption } from '../../../common/config/editorOptions.js';
19
import { EditOperation } from '../../../common/core/editOperation.js';
20
import { IPosition, Position } from '../../../common/core/position.js';
21
import { Range } from '../../../common/core/range.js';
22
import { IEditorContribution, ScrollType } from '../../../common/editorCommon.js';
23
import { EditorContextKeys } from '../../../common/editorContextKeys.js';
24
import { ITextModel, TrackedRangeStickiness } from '../../../common/model.js';
25
import { CompletionItemInsertTextRule, CompletionItemProvider, CompletionTriggerKind, ProviderId } from '../../../common/languages.js';
26
import { SnippetController2 } from '../../snippet/browser/snippetController2.js';
27
import { SnippetParser } from '../../snippet/browser/snippetParser.js';
28
import { ISuggestMemoryService } from './suggestMemory.js';
29
import { WordContextKey } from './wordContextKey.js';
30
import * as nls from '../../../../nls.js';
31
import { CommandsRegistry, ICommandService } from '../../../../platform/commands/common/commands.js';
32
import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js';
33
import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js';
34
import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js';
35
import { ILogService } from '../../../../platform/log/common/log.js';
36
import { CompletionItem, Context as SuggestContext, ISuggestItemPreselector, suggestWidgetStatusbarMenu } from './suggest.js';
37
import { SuggestAlternatives } from './suggestAlternatives.js';
38
import { CommitCharacterController } from './suggestCommitCharacters.js';
39
import { State, SuggestModel } from './suggestModel.js';
40
import { OvertypingCapturer } from './suggestOvertypingCapturer.js';
41
import { ISelectedSuggestion, SuggestWidget } from './suggestWidget.js';
42
import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js';
43
import { basename, extname } from '../../../../base/common/resources.js';
44
import { hash } from '../../../../base/common/hash.js';
45
import { WindowIdleValue, getWindow } from '../../../../base/browser/dom.js';
46
import { ModelDecorationOptions } from '../../../common/model/textModel.js';
47
import { EditSources } from '../../../common/textModelEditSource.js';
48
49
// sticky suggest widget which doesn't disappear on focus out and such
50
const _sticky = false
51
// || Boolean("true") // done "weirdly" so that a lint warning prevents you from pushing this
52
;
53
54
class LineSuffix {
55
56
private readonly _decorationOptions = ModelDecorationOptions.register({
57
description: 'suggest-line-suffix',
58
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
59
});
60
61
private _marker: string | undefined;
62
63
constructor(private readonly _model: ITextModel, private readonly _position: IPosition) {
64
// spy on what's happening right of the cursor. two cases:
65
// 1. end of line -> check that it's still end of line
66
// 2. mid of line -> add a marker and compute the delta
67
const maxColumn = _model.getLineMaxColumn(_position.lineNumber);
68
if (maxColumn !== _position.column) {
69
const offset = _model.getOffsetAt(_position);
70
const end = _model.getPositionAt(offset + 1);
71
_model.changeDecorations(accessor => {
72
if (this._marker) {
73
accessor.removeDecoration(this._marker);
74
}
75
this._marker = accessor.addDecoration(Range.fromPositions(_position, end), this._decorationOptions);
76
});
77
}
78
}
79
80
dispose(): void {
81
if (this._marker && !this._model.isDisposed()) {
82
this._model.changeDecorations(accessor => {
83
accessor.removeDecoration(this._marker!);
84
this._marker = undefined;
85
});
86
}
87
}
88
89
delta(position: IPosition): number {
90
if (this._model.isDisposed() || this._position.lineNumber !== position.lineNumber) {
91
// bail out early if things seems fishy
92
return 0;
93
}
94
// read the marker (in case suggest was triggered at line end) or compare
95
// the cursor to the line end.
96
if (this._marker) {
97
const range = this._model.getDecorationRange(this._marker);
98
const end = this._model.getOffsetAt(range!.getStartPosition());
99
return end - this._model.getOffsetAt(position);
100
} else {
101
return this._model.getLineMaxColumn(position.lineNumber) - position.column;
102
}
103
}
104
}
105
106
const enum InsertFlags {
107
None = 0,
108
NoBeforeUndoStop = 1,
109
NoAfterUndoStop = 2,
110
KeepAlternativeSuggestions = 4,
111
AlternativeOverwriteConfig = 8
112
}
113
114
export class SuggestController implements IEditorContribution {
115
116
public static readonly ID: string = 'editor.contrib.suggestController';
117
118
public static get(editor: ICodeEditor): SuggestController | null {
119
return editor.getContribution<SuggestController>(SuggestController.ID);
120
}
121
122
readonly editor: ICodeEditor;
123
readonly model: SuggestModel;
124
readonly widget: WindowIdleValue<SuggestWidget>;
125
126
private readonly _alternatives: WindowIdleValue<SuggestAlternatives>;
127
private readonly _lineSuffix = new MutableDisposable<LineSuffix>();
128
private readonly _toDispose = new DisposableStore();
129
private readonly _overtypingCapturer: WindowIdleValue<OvertypingCapturer>;
130
private readonly _selectors = new PriorityRegistry<ISuggestItemPreselector>(s => s.priority);
131
132
private readonly _onWillInsertSuggestItem = new Emitter<{ item: CompletionItem }>();
133
get onWillInsertSuggestItem() { return this._onWillInsertSuggestItem.event; }
134
135
private _wantsForceRenderingAbove = false;
136
137
138
constructor(
139
editor: ICodeEditor,
140
@ISuggestMemoryService private readonly _memoryService: ISuggestMemoryService,
141
@ICommandService private readonly _commandService: ICommandService,
142
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
143
@IInstantiationService private readonly _instantiationService: IInstantiationService,
144
@ILogService private readonly _logService: ILogService,
145
@ITelemetryService private readonly _telemetryService: ITelemetryService,
146
) {
147
this.editor = editor;
148
this.model = _instantiationService.createInstance(SuggestModel, this.editor,);
149
150
// default selector
151
this._selectors.register({
152
priority: 0,
153
select: (model, pos, items) => this._memoryService.select(model, pos, items)
154
});
155
156
// context key: update insert/replace mode
157
const ctxInsertMode = SuggestContext.InsertMode.bindTo(_contextKeyService);
158
ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode);
159
this._toDispose.add(this.model.onDidTrigger(() => ctxInsertMode.set(editor.getOption(EditorOption.suggest).insertMode)));
160
161
this.widget = this._toDispose.add(new WindowIdleValue(getWindow(editor.getDomNode()), () => {
162
163
const widget = this._instantiationService.createInstance(SuggestWidget, this.editor);
164
165
this._toDispose.add(widget);
166
this._toDispose.add(widget.onDidSelect(item => this._insertSuggestion(item, InsertFlags.None), this));
167
168
// Wire up logic to accept a suggestion on certain characters
169
const commitCharacterController = new CommitCharacterController(this.editor, widget, this.model, item => this._insertSuggestion(item, InsertFlags.NoAfterUndoStop));
170
this._toDispose.add(commitCharacterController);
171
172
173
// Wire up makes text edit context key
174
const ctxMakesTextEdit = SuggestContext.MakesTextEdit.bindTo(this._contextKeyService);
175
const ctxHasInsertAndReplace = SuggestContext.HasInsertAndReplaceRange.bindTo(this._contextKeyService);
176
const ctxCanResolve = SuggestContext.CanResolve.bindTo(this._contextKeyService);
177
178
this._toDispose.add(toDisposable(() => {
179
ctxMakesTextEdit.reset();
180
ctxHasInsertAndReplace.reset();
181
ctxCanResolve.reset();
182
}));
183
184
this._toDispose.add(widget.onDidFocus(({ item }) => {
185
186
// (ctx: makesTextEdit)
187
const position = this.editor.getPosition()!;
188
const startColumn = item.editStart.column;
189
const endColumn = position.column;
190
let value = true;
191
if (
192
this.editor.getOption(EditorOption.acceptSuggestionOnEnter) === 'smart'
193
&& this.model.state === State.Auto
194
&& !item.completion.additionalTextEdits
195
&& !(item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)
196
&& endColumn - startColumn === item.completion.insertText.length
197
) {
198
const oldText = this.editor.getModel()!.getValueInRange({
199
startLineNumber: position.lineNumber,
200
startColumn,
201
endLineNumber: position.lineNumber,
202
endColumn
203
});
204
value = oldText !== item.completion.insertText;
205
}
206
ctxMakesTextEdit.set(value);
207
208
// (ctx: hasInsertAndReplaceRange)
209
ctxHasInsertAndReplace.set(!Position.equals(item.editInsertEnd, item.editReplaceEnd));
210
211
// (ctx: canResolve)
212
ctxCanResolve.set(Boolean(item.provider.resolveCompletionItem) || Boolean(item.completion.documentation) || item.completion.detail !== item.completion.label);
213
}));
214
215
if (this._wantsForceRenderingAbove) {
216
widget.forceRenderingAbove();
217
}
218
219
return widget;
220
}));
221
222
// Wire up text overtyping capture
223
this._overtypingCapturer = this._toDispose.add(new WindowIdleValue(getWindow(editor.getDomNode()), () => {
224
return this._toDispose.add(new OvertypingCapturer(this.editor, this.model));
225
}));
226
227
this._alternatives = this._toDispose.add(new WindowIdleValue(getWindow(editor.getDomNode()), () => {
228
return this._toDispose.add(new SuggestAlternatives(this.editor, this._contextKeyService));
229
}));
230
231
this._toDispose.add(_instantiationService.createInstance(WordContextKey, editor));
232
233
this._toDispose.add(this.model.onDidTrigger(e => {
234
this.widget.value.showTriggered(e.auto, e.shy ? 250 : 50);
235
this._lineSuffix.value = new LineSuffix(this.editor.getModel()!, e.position);
236
}));
237
this._toDispose.add(this.model.onDidSuggest(e => {
238
if (e.triggerOptions.shy) {
239
return;
240
}
241
let index = -1;
242
for (const selector of this._selectors.itemsOrderedByPriorityDesc) {
243
index = selector.select(this.editor.getModel()!, this.editor.getPosition()!, e.completionModel.items);
244
if (index !== -1) {
245
break;
246
}
247
}
248
if (index === -1) {
249
index = 0;
250
}
251
if (this.model.state === State.Idle) {
252
// selecting an item can "pump" out selection/cursor change events
253
// which can cancel suggest halfway through this function. therefore
254
// we need to check again and bail if the session has been canceled
255
return;
256
}
257
let noFocus = false;
258
if (e.triggerOptions.auto) {
259
// don't "focus" item when configured to do
260
const options = this.editor.getOption(EditorOption.suggest);
261
if (options.selectionMode === 'never' || options.selectionMode === 'always') {
262
// simple: always or never
263
noFocus = options.selectionMode === 'never';
264
265
} else if (options.selectionMode === 'whenTriggerCharacter') {
266
// on with trigger character
267
noFocus = e.triggerOptions.triggerKind !== CompletionTriggerKind.TriggerCharacter;
268
269
} else if (options.selectionMode === 'whenQuickSuggestion') {
270
// without trigger character or when refiltering
271
noFocus = e.triggerOptions.triggerKind === CompletionTriggerKind.TriggerCharacter && !e.triggerOptions.refilter;
272
}
273
274
}
275
this.widget.value.showSuggestions(e.completionModel, index, e.isFrozen, e.triggerOptions.auto, noFocus);
276
}));
277
this._toDispose.add(this.model.onDidCancel(e => {
278
if (!e.retrigger) {
279
this.widget.value.hideWidget();
280
}
281
}));
282
this._toDispose.add(this.editor.onDidBlurEditorWidget(() => {
283
if (!_sticky) {
284
this.model.cancel();
285
this.model.clear();
286
}
287
}));
288
289
// Manage the acceptSuggestionsOnEnter context key
290
const acceptSuggestionsOnEnter = SuggestContext.AcceptSuggestionsOnEnter.bindTo(_contextKeyService);
291
const updateFromConfig = () => {
292
const acceptSuggestionOnEnter = this.editor.getOption(EditorOption.acceptSuggestionOnEnter);
293
acceptSuggestionsOnEnter.set(acceptSuggestionOnEnter === 'on' || acceptSuggestionOnEnter === 'smart');
294
};
295
this._toDispose.add(this.editor.onDidChangeConfiguration(() => updateFromConfig()));
296
updateFromConfig();
297
}
298
299
dispose(): void {
300
this._alternatives.dispose();
301
this._toDispose.dispose();
302
this.widget.dispose();
303
this.model.dispose();
304
this._lineSuffix.dispose();
305
this._onWillInsertSuggestItem.dispose();
306
}
307
308
protected _insertSuggestion(
309
event: ISelectedSuggestion | undefined,
310
flags: InsertFlags
311
): void {
312
if (!event || !event.item) {
313
this._alternatives.value.reset();
314
this.model.cancel();
315
this.model.clear();
316
return;
317
}
318
if (!this.editor.hasModel()) {
319
return;
320
}
321
const snippetController = SnippetController2.get(this.editor);
322
if (!snippetController) {
323
return;
324
}
325
326
this._onWillInsertSuggestItem.fire({ item: event.item });
327
328
const model = this.editor.getModel();
329
const modelVersionNow = model.getAlternativeVersionId();
330
const { item } = event;
331
332
//
333
const tasks: Promise<unknown>[] = [];
334
const cts = new CancellationTokenSource();
335
336
// pushing undo stops *before* additional text edits and
337
// *after* the main edit
338
if (!(flags & InsertFlags.NoBeforeUndoStop)) {
339
this.editor.pushUndoStop();
340
}
341
342
// compute overwrite[Before|After] deltas BEFORE applying extra edits
343
const info = this.getOverwriteInfo(item, Boolean(flags & InsertFlags.AlternativeOverwriteConfig));
344
345
// keep item in memory
346
this._memoryService.memorize(model, this.editor.getPosition(), item);
347
348
const isResolved = item.isResolved;
349
350
// telemetry data points: duration of command execution, info about async additional edits (-1=n/a, -2=none, 1=success, 0=failed)
351
let _commandExectionDuration = -1;
352
let _additionalEditsAppliedAsync = -1;
353
354
if (Array.isArray(item.completion.additionalTextEdits)) {
355
356
// cancel -> stops all listening and closes widget
357
this.model.cancel();
358
359
// sync additional edits
360
const scrollState = StableEditorScrollState.capture(this.editor);
361
this.editor.executeEdits(
362
'suggestController.additionalTextEdits.sync',
363
item.completion.additionalTextEdits.map(edit => {
364
let range = Range.lift(edit.range);
365
if (range.startLineNumber === item.position.lineNumber && range.startColumn > item.position.column) {
366
// shift additional edit when it is "after" the completion insertion position
367
const columnDelta = this.editor.getPosition()!.column - item.position.column;
368
const startColumnDelta = columnDelta;
369
const endColumnDelta = Range.spansMultipleLines(range) ? 0 : columnDelta;
370
range = new Range(range.startLineNumber, range.startColumn + startColumnDelta, range.endLineNumber, range.endColumn + endColumnDelta);
371
}
372
return EditOperation.replaceMove(range, edit.text);
373
})
374
);
375
scrollState.restoreRelativeVerticalPositionOfCursor(this.editor);
376
377
} else if (!isResolved) {
378
// async additional edits
379
const sw = new StopWatch();
380
let position: IPosition | undefined;
381
382
const docListener = model.onDidChangeContent(e => {
383
if (e.isFlush) {
384
cts.cancel();
385
docListener.dispose();
386
return;
387
}
388
for (const change of e.changes) {
389
const thisPosition = Range.getEndPosition(change.range);
390
if (!position || Position.isBefore(thisPosition, position)) {
391
position = thisPosition;
392
}
393
}
394
});
395
396
const oldFlags = flags;
397
flags |= InsertFlags.NoAfterUndoStop;
398
let didType = false;
399
const typeListener = this.editor.onWillType(() => {
400
typeListener.dispose();
401
didType = true;
402
if (!(oldFlags & InsertFlags.NoAfterUndoStop)) {
403
this.editor.pushUndoStop();
404
}
405
});
406
407
tasks.push(item.resolve(cts.token).then(() => {
408
if (!item.completion.additionalTextEdits || cts.token.isCancellationRequested) {
409
return undefined;
410
}
411
if (position && item.completion.additionalTextEdits.some(edit => Position.isBefore(position!, Range.getStartPosition(edit.range)))) {
412
return false;
413
}
414
if (didType) {
415
this.editor.pushUndoStop();
416
}
417
const scrollState = StableEditorScrollState.capture(this.editor);
418
this.editor.executeEdits(
419
'suggestController.additionalTextEdits.async',
420
item.completion.additionalTextEdits.map(edit => EditOperation.replaceMove(Range.lift(edit.range), edit.text))
421
);
422
scrollState.restoreRelativeVerticalPositionOfCursor(this.editor);
423
if (didType || !(oldFlags & InsertFlags.NoAfterUndoStop)) {
424
this.editor.pushUndoStop();
425
}
426
return true;
427
}).then(applied => {
428
this._logService.trace('[suggest] async resolving of edits DONE (ms, applied?)', sw.elapsed(), applied);
429
_additionalEditsAppliedAsync = applied === true ? 1 : applied === false ? 0 : -2;
430
}).finally(() => {
431
docListener.dispose();
432
typeListener.dispose();
433
}));
434
}
435
436
let { insertText } = item.completion;
437
if (!(item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet)) {
438
insertText = SnippetParser.escape(insertText);
439
}
440
441
// cancel -> stops all listening and closes widget
442
this.model.cancel();
443
444
snippetController.insert(insertText, {
445
overwriteBefore: info.overwriteBefore,
446
overwriteAfter: info.overwriteAfter,
447
undoStopBefore: false,
448
undoStopAfter: false,
449
adjustWhitespace: !(item.completion.insertTextRules! & CompletionItemInsertTextRule.KeepWhitespace),
450
clipboardText: event.model.clipboardText,
451
overtypingCapturer: this._overtypingCapturer.value,
452
reason: EditSources.suggest({ providerId: ProviderId.fromExtensionId(item.extensionId?.value) }),
453
});
454
455
if (!(flags & InsertFlags.NoAfterUndoStop)) {
456
this.editor.pushUndoStop();
457
}
458
459
if (item.completion.command) {
460
if (item.completion.command.id === TriggerSuggestAction.id) {
461
// retigger
462
this.model.trigger({ auto: true, retrigger: true });
463
} else {
464
// exec command, done
465
const sw = new StopWatch();
466
tasks.push(this._commandService.executeCommand(item.completion.command.id, ...(item.completion.command.arguments ? [...item.completion.command.arguments] : [])).catch(e => {
467
if (item.completion.extensionId) {
468
onUnexpectedExternalError(e);
469
} else {
470
onUnexpectedError(e);
471
}
472
}).finally(() => {
473
_commandExectionDuration = sw.elapsed();
474
}));
475
}
476
}
477
478
if (flags & InsertFlags.KeepAlternativeSuggestions) {
479
this._alternatives.value.set(event, next => {
480
481
// cancel resolving of additional edits
482
cts.cancel();
483
484
// this is not so pretty. when inserting the 'next'
485
// suggestion we undo until we are at the state at
486
// which we were before inserting the previous suggestion...
487
while (model.canUndo()) {
488
if (modelVersionNow !== model.getAlternativeVersionId()) {
489
model.undo();
490
}
491
this._insertSuggestion(
492
next,
493
InsertFlags.NoBeforeUndoStop | InsertFlags.NoAfterUndoStop | (flags & InsertFlags.AlternativeOverwriteConfig ? InsertFlags.AlternativeOverwriteConfig : 0)
494
);
495
break;
496
}
497
});
498
}
499
500
this._alertCompletionItem(item);
501
502
// clear only now - after all tasks are done
503
Promise.all(tasks).finally(() => {
504
this._reportSuggestionAcceptedTelemetry(item, model, isResolved, _commandExectionDuration, _additionalEditsAppliedAsync, event.index, event.model.items);
505
506
this.model.clear();
507
cts.dispose();
508
});
509
}
510
511
private _reportSuggestionAcceptedTelemetry(item: CompletionItem, model: ITextModel, itemResolved: boolean, commandExectionDuration: number, additionalEditsAppliedAsync: number, index: number, completionItems: CompletionItem[]): void {
512
if (Math.random() > 0.0001) { // 0.01%
513
return;
514
}
515
516
const labelMap = new Map<string, number[]>();
517
518
for (let i = 0; i < Math.min(30, completionItems.length); i++) {
519
const label = completionItems[i].textLabel;
520
521
if (labelMap.has(label)) {
522
labelMap.get(label)!.push(i);
523
} else {
524
labelMap.set(label, [i]);
525
}
526
}
527
528
const firstIndexArray = labelMap.get(item.textLabel);
529
const hasDuplicates = firstIndexArray && firstIndexArray.length > 1;
530
const firstIndex = hasDuplicates ? firstIndexArray[0] : -1;
531
532
type AcceptedSuggestion = {
533
extensionId: string; providerId: string;
534
fileExtension: string; languageId: string; basenameHash: string; kind: number;
535
resolveInfo: number; resolveDuration: number;
536
commandDuration: number;
537
additionalEditsAsync: number;
538
index: number; firstIndex: number;
539
};
540
type AcceptedSuggestionClassification = {
541
owner: 'jrieken';
542
comment: 'Information accepting completion items';
543
extensionId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'Extension contributing the completions item' };
544
providerId: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'Provider of the completions item' };
545
basenameHash: { classification: 'PublicNonPersonalData'; purpose: 'FeatureInsight'; comment: 'Hash of the basename of the file into which the completion was inserted' };
546
fileExtension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File extension of the file into which the completion was inserted' };
547
languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Language type of the file into which the completion was inserted' };
548
kind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The completion item kind' };
549
resolveInfo: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'If the item was inserted before resolving was done' };
550
resolveDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'How long resolving took to finish' };
551
commandDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'How long a completion item command took' };
552
additionalEditsAsync: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Info about asynchronously applying additional edits' };
553
index: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The index of the completion item in the sorted list.' };
554
firstIndex: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'When there are multiple completions, the index of the first instance.' };
555
};
556
557
this._telemetryService.publicLog2<AcceptedSuggestion, AcceptedSuggestionClassification>('suggest.acceptedSuggestion', {
558
extensionId: item.extensionId?.value ?? 'unknown',
559
providerId: item.provider._debugDisplayName ?? 'unknown',
560
kind: item.completion.kind,
561
basenameHash: hash(basename(model.uri)).toString(16),
562
languageId: model.getLanguageId(),
563
fileExtension: extname(model.uri),
564
resolveInfo: !item.provider.resolveCompletionItem ? -1 : itemResolved ? 1 : 0,
565
resolveDuration: item.resolveDuration,
566
commandDuration: commandExectionDuration,
567
additionalEditsAsync: additionalEditsAppliedAsync,
568
index,
569
firstIndex,
570
});
571
}
572
573
getOverwriteInfo(item: CompletionItem, toggleMode: boolean): { overwriteBefore: number; overwriteAfter: number } {
574
assertType(this.editor.hasModel());
575
576
let replace = this.editor.getOption(EditorOption.suggest).insertMode === 'replace';
577
if (toggleMode) {
578
replace = !replace;
579
}
580
const overwriteBefore = item.position.column - item.editStart.column;
581
const overwriteAfter = (replace ? item.editReplaceEnd.column : item.editInsertEnd.column) - item.position.column;
582
const columnDelta = this.editor.getPosition().column - item.position.column;
583
const suffixDelta = this._lineSuffix.value ? this._lineSuffix.value.delta(this.editor.getPosition()) : 0;
584
585
return {
586
overwriteBefore: overwriteBefore + columnDelta,
587
overwriteAfter: overwriteAfter + suffixDelta
588
};
589
}
590
591
private _alertCompletionItem(item: CompletionItem): void {
592
if (isNonEmptyArray(item.completion.additionalTextEdits)) {
593
const msg = nls.localize('aria.alert.snippet', "Accepting '{0}' made {1} additional edits", item.textLabel, item.completion.additionalTextEdits.length);
594
alert(msg);
595
}
596
}
597
598
triggerSuggest(onlyFrom?: Set<CompletionItemProvider>, auto?: boolean, noFilter?: boolean): void {
599
if (this.editor.hasModel()) {
600
this.model.trigger({
601
auto: auto ?? false,
602
completionOptions: { providerFilter: onlyFrom, kindFilter: noFilter ? new Set() : undefined }
603
});
604
this.editor.revealPosition(this.editor.getPosition(), ScrollType.Smooth);
605
this.editor.focus();
606
}
607
}
608
609
triggerSuggestAndAcceptBest(arg: { fallback: string }): void {
610
if (!this.editor.hasModel()) {
611
return;
612
613
}
614
const positionNow = this.editor.getPosition();
615
616
const fallback = () => {
617
if (positionNow.equals(this.editor.getPosition()!)) {
618
this._commandService.executeCommand(arg.fallback);
619
}
620
};
621
622
const makesTextEdit = (item: CompletionItem): boolean => {
623
if (item.completion.insertTextRules! & CompletionItemInsertTextRule.InsertAsSnippet || item.completion.additionalTextEdits) {
624
// snippet, other editor -> makes edit
625
return true;
626
}
627
const position = this.editor.getPosition()!;
628
const startColumn = item.editStart.column;
629
const endColumn = position.column;
630
if (endColumn - startColumn !== item.completion.insertText.length) {
631
// unequal lengths -> makes edit
632
return true;
633
}
634
const textNow = this.editor.getModel()!.getValueInRange({
635
startLineNumber: position.lineNumber,
636
startColumn,
637
endLineNumber: position.lineNumber,
638
endColumn
639
});
640
// unequal text -> makes edit
641
return textNow !== item.completion.insertText;
642
};
643
644
Event.once(this.model.onDidTrigger)(_ => {
645
// wait for trigger because only then the cancel-event is trustworthy
646
const listener: IDisposable[] = [];
647
648
Event.any<unknown>(this.model.onDidTrigger, this.model.onDidCancel)(() => {
649
// retrigger or cancel -> try to type default text
650
dispose(listener);
651
fallback();
652
}, undefined, listener);
653
654
this.model.onDidSuggest(({ completionModel }) => {
655
dispose(listener);
656
if (completionModel.items.length === 0) {
657
fallback();
658
return;
659
}
660
const index = this._memoryService.select(this.editor.getModel()!, this.editor.getPosition()!, completionModel.items);
661
const item = completionModel.items[index];
662
if (!makesTextEdit(item)) {
663
fallback();
664
return;
665
}
666
this.editor.pushUndoStop();
667
this._insertSuggestion({ index, item, model: completionModel }, InsertFlags.KeepAlternativeSuggestions | InsertFlags.NoBeforeUndoStop | InsertFlags.NoAfterUndoStop);
668
669
}, undefined, listener);
670
});
671
672
this.model.trigger({ auto: false, shy: true });
673
this.editor.revealPosition(positionNow, ScrollType.Smooth);
674
this.editor.focus();
675
}
676
677
acceptSelectedSuggestion(keepAlternativeSuggestions: boolean, alternativeOverwriteConfig: boolean): void {
678
const item = this.widget.value.getFocusedItem();
679
let flags = 0;
680
if (keepAlternativeSuggestions) {
681
flags |= InsertFlags.KeepAlternativeSuggestions;
682
}
683
if (alternativeOverwriteConfig) {
684
flags |= InsertFlags.AlternativeOverwriteConfig;
685
}
686
this._insertSuggestion(item, flags);
687
}
688
689
acceptNextSuggestion() {
690
this._alternatives.value.next();
691
}
692
693
acceptPrevSuggestion() {
694
this._alternatives.value.prev();
695
}
696
697
cancelSuggestWidget(): void {
698
this.model.cancel();
699
this.model.clear();
700
this.widget.value.hideWidget();
701
}
702
703
focusSuggestion(): void {
704
this.widget.value.focusSelected();
705
}
706
707
selectNextSuggestion(): void {
708
this.widget.value.selectNext();
709
}
710
711
selectNextPageSuggestion(): void {
712
this.widget.value.selectNextPage();
713
}
714
715
selectLastSuggestion(): void {
716
this.widget.value.selectLast();
717
}
718
719
selectPrevSuggestion(): void {
720
this.widget.value.selectPrevious();
721
}
722
723
selectPrevPageSuggestion(): void {
724
this.widget.value.selectPreviousPage();
725
}
726
727
selectFirstSuggestion(): void {
728
this.widget.value.selectFirst();
729
}
730
731
toggleSuggestionDetails(): void {
732
this.widget.value.toggleDetails();
733
}
734
735
toggleExplainMode(): void {
736
this.widget.value.toggleExplainMode();
737
}
738
739
toggleSuggestionFocus(): void {
740
this.widget.value.toggleDetailsFocus();
741
}
742
743
resetWidgetSize(): void {
744
this.widget.value.resetPersistedSize();
745
}
746
747
forceRenderingAbove() {
748
if (this.widget.isInitialized) {
749
this.widget.value.forceRenderingAbove();
750
} else {
751
// Defer this until the widget is created
752
this._wantsForceRenderingAbove = true;
753
}
754
}
755
756
stopForceRenderingAbove() {
757
if (this.widget.isInitialized) {
758
this.widget.value.stopForceRenderingAbove();
759
} else {
760
this._wantsForceRenderingAbove = false;
761
}
762
}
763
764
registerSelector(selector: ISuggestItemPreselector): IDisposable {
765
return this._selectors.register(selector);
766
}
767
}
768
769
class PriorityRegistry<T> {
770
private readonly _items = new Array<T>();
771
772
constructor(private readonly prioritySelector: (item: T) => number) { }
773
774
register(value: T): IDisposable {
775
if (this._items.indexOf(value) !== -1) {
776
throw new Error('Value is already registered');
777
}
778
this._items.push(value);
779
this._items.sort((s1, s2) => this.prioritySelector(s2) - this.prioritySelector(s1));
780
781
return {
782
dispose: () => {
783
const idx = this._items.indexOf(value);
784
if (idx >= 0) {
785
this._items.splice(idx, 1);
786
}
787
}
788
};
789
}
790
791
get itemsOrderedByPriorityDesc(): readonly T[] {
792
return this._items;
793
}
794
}
795
796
export class TriggerSuggestAction extends EditorAction {
797
798
static readonly id = 'editor.action.triggerSuggest';
799
800
constructor() {
801
super({
802
id: TriggerSuggestAction.id,
803
label: nls.localize2('suggest.trigger.label', "Trigger Suggest"),
804
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasCompletionItemProvider, SuggestContext.Visible.toNegated()),
805
kbOpts: {
806
kbExpr: EditorContextKeys.textInputFocus,
807
primary: KeyMod.CtrlCmd | KeyCode.Space,
808
secondary: [KeyMod.CtrlCmd | KeyCode.KeyI],
809
mac: { primary: KeyMod.WinCtrl | KeyCode.Space, secondary: [KeyMod.Alt | KeyCode.Escape, KeyMod.CtrlCmd | KeyCode.KeyI] },
810
weight: KeybindingWeight.EditorContrib
811
}
812
});
813
}
814
815
run(_accessor: ServicesAccessor, editor: ICodeEditor, args: unknown): void {
816
const controller = SuggestController.get(editor);
817
818
if (!controller) {
819
return;
820
}
821
822
type TriggerArgs = { auto: boolean };
823
let auto: boolean | undefined;
824
if (args && typeof args === 'object') {
825
if ((<TriggerArgs>args).auto === true) {
826
auto = true;
827
}
828
}
829
830
controller.triggerSuggest(undefined, auto, undefined);
831
}
832
}
833
834
registerEditorContribution(SuggestController.ID, SuggestController, EditorContributionInstantiation.BeforeFirstInteraction);
835
registerEditorAction(TriggerSuggestAction);
836
837
const weight = KeybindingWeight.EditorContrib + 90;
838
839
const SuggestCommand = EditorCommand.bindToContribution<SuggestController>(SuggestController.get);
840
841
842
registerEditorCommand(new SuggestCommand({
843
id: 'acceptSelectedSuggestion',
844
precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.HasFocusedSuggestion),
845
handler(x) {
846
x.acceptSelectedSuggestion(true, false);
847
},
848
kbOpts: [{
849
// normal tab
850
primary: KeyCode.Tab,
851
kbExpr: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus),
852
weight,
853
}, {
854
// accept on enter has special rules
855
primary: KeyCode.Enter,
856
kbExpr: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus, SuggestContext.AcceptSuggestionsOnEnter, SuggestContext.MakesTextEdit),
857
weight,
858
}],
859
menuOpts: [{
860
menuId: suggestWidgetStatusbarMenu,
861
title: nls.localize('accept.insert', "Insert"),
862
group: 'left',
863
order: 1,
864
when: ContextKeyExpr.and(SuggestContext.HasFocusedSuggestion, SuggestContext.HasInsertAndReplaceRange.toNegated())
865
}, {
866
menuId: suggestWidgetStatusbarMenu,
867
title: nls.localize('accept.insert', "Insert"),
868
group: 'left',
869
order: 1,
870
when: ContextKeyExpr.and(SuggestContext.HasFocusedSuggestion, SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('insert'))
871
}, {
872
menuId: suggestWidgetStatusbarMenu,
873
title: nls.localize('accept.replace', "Replace"),
874
group: 'left',
875
order: 1,
876
when: ContextKeyExpr.and(SuggestContext.HasFocusedSuggestion, SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('replace'))
877
}]
878
}));
879
880
registerEditorCommand(new SuggestCommand({
881
id: 'acceptAlternativeSelectedSuggestion',
882
precondition: ContextKeyExpr.and(SuggestContext.Visible, EditorContextKeys.textInputFocus, SuggestContext.HasFocusedSuggestion),
883
kbOpts: {
884
weight: weight,
885
kbExpr: EditorContextKeys.textInputFocus,
886
primary: KeyMod.Shift | KeyCode.Enter,
887
secondary: [KeyMod.Shift | KeyCode.Tab],
888
},
889
handler(x) {
890
x.acceptSelectedSuggestion(false, true);
891
},
892
menuOpts: [{
893
menuId: suggestWidgetStatusbarMenu,
894
group: 'left',
895
order: 2,
896
when: ContextKeyExpr.and(SuggestContext.HasFocusedSuggestion, SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('insert')),
897
title: nls.localize('accept.replace', "Replace")
898
}, {
899
menuId: suggestWidgetStatusbarMenu,
900
group: 'left',
901
order: 2,
902
when: ContextKeyExpr.and(SuggestContext.HasFocusedSuggestion, SuggestContext.HasInsertAndReplaceRange, SuggestContext.InsertMode.isEqualTo('replace')),
903
title: nls.localize('accept.insert', "Insert")
904
}]
905
}));
906
907
908
// continue to support the old command
909
CommandsRegistry.registerCommandAlias('acceptSelectedSuggestionOnEnter', 'acceptSelectedSuggestion');
910
911
registerEditorCommand(new SuggestCommand({
912
id: 'hideSuggestWidget',
913
precondition: SuggestContext.Visible,
914
handler: x => x.cancelSuggestWidget(),
915
kbOpts: {
916
weight: weight,
917
kbExpr: EditorContextKeys.textInputFocus,
918
primary: KeyCode.Escape,
919
secondary: [KeyMod.Shift | KeyCode.Escape]
920
}
921
}));
922
923
registerEditorCommand(new SuggestCommand({
924
id: 'selectNextSuggestion',
925
precondition: ContextKeyExpr.and(SuggestContext.Visible, ContextKeyExpr.or(SuggestContext.MultipleSuggestions, SuggestContext.HasFocusedSuggestion.negate())),
926
handler: c => c.selectNextSuggestion(),
927
kbOpts: {
928
weight: weight,
929
kbExpr: EditorContextKeys.textInputFocus,
930
primary: KeyCode.DownArrow,
931
secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow],
932
mac: { primary: KeyCode.DownArrow, secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow, KeyMod.WinCtrl | KeyCode.KeyN] }
933
},
934
menuOpts: {
935
menuId: suggestWidgetStatusbarMenu,
936
group: 'left',
937
order: 0,
938
when: SuggestContext.HasFocusedSuggestion.toNegated(),
939
title: nls.localize('focus.suggestion', "Select")
940
}
941
}));
942
943
registerEditorCommand(new SuggestCommand({
944
id: 'selectNextPageSuggestion',
945
precondition: ContextKeyExpr.and(SuggestContext.Visible, ContextKeyExpr.or(SuggestContext.MultipleSuggestions, SuggestContext.HasFocusedSuggestion.negate())),
946
handler: c => c.selectNextPageSuggestion(),
947
kbOpts: {
948
weight: weight,
949
kbExpr: EditorContextKeys.textInputFocus,
950
primary: KeyCode.PageDown,
951
secondary: [KeyMod.CtrlCmd | KeyCode.PageDown]
952
}
953
}));
954
955
registerEditorCommand(new SuggestCommand({
956
id: 'selectLastSuggestion',
957
precondition: ContextKeyExpr.and(SuggestContext.Visible, ContextKeyExpr.or(SuggestContext.MultipleSuggestions, SuggestContext.HasFocusedSuggestion.negate())),
958
handler: c => c.selectLastSuggestion()
959
}));
960
961
registerEditorCommand(new SuggestCommand({
962
id: 'selectPrevSuggestion',
963
precondition: ContextKeyExpr.and(SuggestContext.Visible, ContextKeyExpr.or(SuggestContext.MultipleSuggestions, SuggestContext.HasFocusedSuggestion.negate())),
964
handler: c => c.selectPrevSuggestion(),
965
kbOpts: {
966
weight: weight,
967
kbExpr: EditorContextKeys.textInputFocus,
968
primary: KeyCode.UpArrow,
969
secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow],
970
mac: { primary: KeyCode.UpArrow, secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow, KeyMod.WinCtrl | KeyCode.KeyP] }
971
}
972
}));
973
974
registerEditorCommand(new SuggestCommand({
975
id: 'selectPrevPageSuggestion',
976
precondition: ContextKeyExpr.and(SuggestContext.Visible, ContextKeyExpr.or(SuggestContext.MultipleSuggestions, SuggestContext.HasFocusedSuggestion.negate())),
977
handler: c => c.selectPrevPageSuggestion(),
978
kbOpts: {
979
weight: weight,
980
kbExpr: EditorContextKeys.textInputFocus,
981
primary: KeyCode.PageUp,
982
secondary: [KeyMod.CtrlCmd | KeyCode.PageUp]
983
}
984
}));
985
986
registerEditorCommand(new SuggestCommand({
987
id: 'selectFirstSuggestion',
988
precondition: ContextKeyExpr.and(SuggestContext.Visible, ContextKeyExpr.or(SuggestContext.MultipleSuggestions, SuggestContext.HasFocusedSuggestion.negate())),
989
handler: c => c.selectFirstSuggestion()
990
}));
991
992
registerEditorCommand(new SuggestCommand({
993
id: 'focusSuggestion',
994
precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.HasFocusedSuggestion.negate()),
995
handler: x => x.focusSuggestion(),
996
kbOpts: {
997
weight: weight,
998
kbExpr: EditorContextKeys.textInputFocus,
999
primary: KeyMod.CtrlCmd | KeyCode.Space,
1000
secondary: [KeyMod.CtrlCmd | KeyCode.KeyI],
1001
mac: { primary: KeyMod.WinCtrl | KeyCode.Space, secondary: [KeyMod.CtrlCmd | KeyCode.KeyI] }
1002
},
1003
}));
1004
1005
registerEditorCommand(new SuggestCommand({
1006
id: 'focusAndAcceptSuggestion',
1007
precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.HasFocusedSuggestion.negate()),
1008
handler: c => {
1009
c.focusSuggestion();
1010
c.acceptSelectedSuggestion(true, false);
1011
}
1012
}));
1013
1014
registerEditorCommand(new SuggestCommand({
1015
id: 'toggleSuggestionDetails',
1016
precondition: ContextKeyExpr.and(SuggestContext.Visible, SuggestContext.HasFocusedSuggestion),
1017
handler: x => x.toggleSuggestionDetails(),
1018
kbOpts: {
1019
weight: weight,
1020
kbExpr: EditorContextKeys.textInputFocus,
1021
primary: KeyMod.CtrlCmd | KeyCode.Space,
1022
secondary: [KeyMod.CtrlCmd | KeyCode.KeyI],
1023
mac: { primary: KeyMod.WinCtrl | KeyCode.Space, secondary: [KeyMod.CtrlCmd | KeyCode.KeyI] }
1024
},
1025
menuOpts: [{
1026
menuId: suggestWidgetStatusbarMenu,
1027
group: 'right',
1028
order: 1,
1029
when: ContextKeyExpr.and(SuggestContext.DetailsVisible, SuggestContext.CanResolve),
1030
title: nls.localize('detail.more', "Show Less")
1031
}, {
1032
menuId: suggestWidgetStatusbarMenu,
1033
group: 'right',
1034
order: 1,
1035
when: ContextKeyExpr.and(SuggestContext.DetailsVisible.toNegated(), SuggestContext.CanResolve),
1036
title: nls.localize('detail.less', "Show More")
1037
}]
1038
}));
1039
1040
registerEditorCommand(new SuggestCommand({
1041
id: 'toggleExplainMode',
1042
precondition: SuggestContext.Visible,
1043
handler: x => x.toggleExplainMode(),
1044
kbOpts: {
1045
weight: KeybindingWeight.EditorContrib,
1046
primary: KeyMod.CtrlCmd | KeyCode.Slash,
1047
}
1048
}));
1049
1050
registerEditorCommand(new SuggestCommand({
1051
id: 'toggleSuggestionFocus',
1052
precondition: SuggestContext.Visible,
1053
handler: x => x.toggleSuggestionFocus(),
1054
kbOpts: {
1055
weight: weight,
1056
kbExpr: EditorContextKeys.textInputFocus,
1057
primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Space,
1058
mac: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Space }
1059
}
1060
}));
1061
1062
//#region tab completions
1063
1064
registerEditorCommand(new SuggestCommand({
1065
id: 'insertBestCompletion',
1066
precondition: ContextKeyExpr.and(
1067
EditorContextKeys.textInputFocus,
1068
ContextKeyExpr.equals('config.editor.tabCompletion', 'on'),
1069
WordContextKey.AtEnd,
1070
SuggestContext.Visible.toNegated(),
1071
SuggestAlternatives.OtherSuggestions.toNegated(),
1072
SnippetController2.InSnippetMode.toNegated()
1073
),
1074
handler: (x, arg) => {
1075
1076
x.triggerSuggestAndAcceptBest(isObject(arg) ? { fallback: 'tab', ...arg } : { fallback: 'tab' });
1077
},
1078
kbOpts: {
1079
weight,
1080
primary: KeyCode.Tab
1081
}
1082
}));
1083
1084
registerEditorCommand(new SuggestCommand({
1085
id: 'insertNextSuggestion',
1086
precondition: ContextKeyExpr.and(
1087
EditorContextKeys.textInputFocus,
1088
ContextKeyExpr.equals('config.editor.tabCompletion', 'on'),
1089
SuggestAlternatives.OtherSuggestions,
1090
SuggestContext.Visible.toNegated(),
1091
SnippetController2.InSnippetMode.toNegated()
1092
),
1093
handler: x => x.acceptNextSuggestion(),
1094
kbOpts: {
1095
weight: weight,
1096
kbExpr: EditorContextKeys.textInputFocus,
1097
primary: KeyCode.Tab
1098
}
1099
}));
1100
1101
registerEditorCommand(new SuggestCommand({
1102
id: 'insertPrevSuggestion',
1103
precondition: ContextKeyExpr.and(
1104
EditorContextKeys.textInputFocus,
1105
ContextKeyExpr.equals('config.editor.tabCompletion', 'on'),
1106
SuggestAlternatives.OtherSuggestions,
1107
SuggestContext.Visible.toNegated(),
1108
SnippetController2.InSnippetMode.toNegated()
1109
),
1110
handler: x => x.acceptPrevSuggestion(),
1111
kbOpts: {
1112
weight: weight,
1113
kbExpr: EditorContextKeys.textInputFocus,
1114
primary: KeyMod.Shift | KeyCode.Tab
1115
}
1116
}));
1117
1118
1119
registerEditorCommand(new class extends EditorCommand {
1120
constructor() {
1121
super({
1122
id: 'suggestWidgetCopy',
1123
precondition: SuggestContext.DetailsFocused,
1124
kbOpts: {
1125
weight: weight + 10,
1126
kbExpr: SuggestContext.DetailsFocused,
1127
primary: KeyMod.CtrlCmd | KeyCode.KeyC,
1128
win: { primary: KeyMod.CtrlCmd | KeyCode.KeyC, secondary: [KeyMod.CtrlCmd | KeyCode.Insert] }
1129
}
1130
});
1131
}
1132
runEditorCommand(_accessor: ServicesAccessor, editor: ICodeEditor) {
1133
getWindow(editor.getDomNode()).document.execCommand('copy');
1134
}
1135
}());
1136
1137
registerEditorAction(class extends EditorAction {
1138
1139
constructor() {
1140
super({
1141
id: 'editor.action.resetSuggestSize',
1142
label: nls.localize2('suggest.reset.label', "Reset Suggest Widget Size"),
1143
precondition: undefined
1144
});
1145
}
1146
1147
run(_accessor: ServicesAccessor, editor: ICodeEditor): void {
1148
SuggestController.get(editor)?.resetWidgetSize();
1149
}
1150
});
1151
1152