Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts
4798 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 { assertNever } from '../../../../../base/common/assert.js';
7
import { AsyncIterableProducer } from '../../../../../base/common/async.js';
8
import { CancellationToken, CancellationTokenSource } from '../../../../../base/common/cancellation.js';
9
import { BugIndicatingError, onUnexpectedExternalError } from '../../../../../base/common/errors.js';
10
import { Disposable, IDisposable } from '../../../../../base/common/lifecycle.js';
11
import { prefixedUuid } from '../../../../../base/common/uuid.js';
12
import { ICommandService } from '../../../../../platform/commands/common/commands.js';
13
import { ISingleEditOperation } from '../../../../common/core/editOperation.js';
14
import { StringReplacement } from '../../../../common/core/edits/stringEdit.js';
15
import { OffsetRange } from '../../../../common/core/ranges/offsetRange.js';
16
import { Position } from '../../../../common/core/position.js';
17
import { Range } from '../../../../common/core/range.js';
18
import { TextReplacement } from '../../../../common/core/edits/textEdit.js';
19
import { InlineCompletionEndOfLifeReason, InlineCompletionEndOfLifeReasonKind, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, PartialAcceptInfo, InlineCompletionsDisposeReason, LifetimeSummary, ProviderId, IInlineCompletionHint } from '../../../../common/languages.js';
20
import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js';
21
import { ITextModel } from '../../../../common/model.js';
22
import { fixBracketsInLine } from '../../../../common/model/bracketPairsTextModelPart/fixBrackets.js';
23
import { SnippetParser, Text } from '../../../snippet/browser/snippetParser.js';
24
import { ErrorResult, getReadonlyEmptyArray } from '../utils.js';
25
import { groupByMap } from '../../../../../base/common/collections.js';
26
import { DirectedGraph } from './graph.js';
27
import { CachedFunction } from '../../../../../base/common/cache.js';
28
import { InlineCompletionViewData, InlineCompletionViewKind } from '../view/inlineEdits/inlineEditsViewInterface.js';
29
import { isDefined } from '../../../../../base/common/types.js';
30
import { inlineCompletionIsVisible } from './inlineCompletionIsVisible.js';
31
import { EditDeltaInfo } from '../../../../common/textModelEditSource.js';
32
import { URI } from '../../../../../base/common/uri.js';
33
import { InlineSuggestionEditKind } from './editKind.js';
34
import { InlineSuggestAlternativeAction } from './InlineSuggestAlternativeAction.js';
35
36
export type InlineCompletionContextWithoutUuid = Omit<InlineCompletionContext, 'requestUuid'>;
37
38
export function provideInlineCompletions(
39
providers: InlineCompletionsProvider[],
40
position: Position,
41
model: ITextModel,
42
context: InlineCompletionContextWithoutUuid,
43
requestInfo: InlineSuggestRequestInfo,
44
languageConfigurationService?: ILanguageConfigurationService,
45
): IInlineCompletionProviderResult {
46
const requestUuid = prefixedUuid('icr');
47
48
const cancellationTokenSource = new CancellationTokenSource();
49
let cancelReason: InlineCompletionsDisposeReason | undefined = undefined;
50
51
const contextWithUuid: InlineCompletionContext = { ...context, requestUuid: requestUuid };
52
53
const defaultReplaceRange = getDefaultRange(position, model);
54
55
const providersByGroupId = groupByMap(providers, p => p.groupId);
56
const yieldsToGraph = DirectedGraph.from(providers, p => {
57
return p.yieldsToGroupIds?.flatMap(groupId => providersByGroupId.get(groupId) ?? []) ?? [];
58
});
59
const { foundCycles } = yieldsToGraph.removeCycles();
60
if (foundCycles.length > 0) {
61
onUnexpectedExternalError(new Error(`Inline completions: cyclic yield-to dependency detected.`
62
+ ` Path: ${foundCycles.map(s => s.toString ? s.toString() : ('' + s)).join(' -> ')}`));
63
}
64
65
let runningCount = 0;
66
67
const queryProvider = new CachedFunction(async (provider: InlineCompletionsProvider<InlineCompletions>): Promise<InlineSuggestionList | undefined> => {
68
try {
69
runningCount++;
70
if (cancellationTokenSource.token.isCancellationRequested) {
71
return undefined;
72
}
73
74
const yieldsTo = yieldsToGraph.getOutgoing(provider);
75
for (const p of yieldsTo) {
76
// We know there is no cycle, so no recursion here
77
const result = await queryProvider.get(p);
78
if (result) {
79
for (const item of result.inlineSuggestions.items) {
80
if (item.isInlineEdit || typeof item.insertText !== 'string' && item.insertText !== undefined) {
81
return undefined;
82
}
83
if (item.insertText !== undefined) {
84
const t = new TextReplacement(Range.lift(item.range) ?? defaultReplaceRange, item.insertText);
85
if (inlineCompletionIsVisible(t, undefined, model, position)) {
86
return undefined;
87
}
88
}
89
90
// else: inline completion is not visible, so lets not block
91
}
92
}
93
}
94
95
let result: InlineCompletions | null | undefined;
96
const providerStartTime = Date.now();
97
try {
98
result = await provider.provideInlineCompletions(model, position, contextWithUuid, cancellationTokenSource.token);
99
} catch (e) {
100
onUnexpectedExternalError(e);
101
return undefined;
102
}
103
const providerEndTime = Date.now();
104
105
if (!result) {
106
return undefined;
107
}
108
109
const data: InlineSuggestData[] = [];
110
const list = new InlineSuggestionList(result, data, provider);
111
list.addRef();
112
runWhenCancelled(cancellationTokenSource.token, () => {
113
return list.removeRef(cancelReason);
114
});
115
if (cancellationTokenSource.token.isCancellationRequested) {
116
return undefined; // The list is disposed now, so we cannot return the items!
117
}
118
119
for (const item of result.items) {
120
const r = toInlineSuggestData(item, list, defaultReplaceRange, model, languageConfigurationService, contextWithUuid, requestInfo, { startTime: providerStartTime, endTime: providerEndTime });
121
if (ErrorResult.is(r)) {
122
r.logError();
123
continue;
124
}
125
data.push(r);
126
}
127
128
return list;
129
} finally {
130
runningCount--;
131
}
132
});
133
134
const inlineCompletionLists = AsyncIterableProducer.fromPromisesResolveOrder(providers.map(p => queryProvider.get(p))).filter(isDefined);
135
136
return {
137
contextWithUuid,
138
get didAllProvidersReturn() { return runningCount === 0; },
139
lists: inlineCompletionLists,
140
cancelAndDispose: reason => {
141
if (cancelReason !== undefined) {
142
return;
143
}
144
cancelReason = reason;
145
cancellationTokenSource.dispose(true);
146
}
147
};
148
}
149
150
/** If the token is eventually cancelled, this will not leak either. */
151
export function runWhenCancelled(token: CancellationToken, callback: () => void): IDisposable {
152
if (token.isCancellationRequested) {
153
callback();
154
return Disposable.None;
155
} else {
156
const listener = token.onCancellationRequested(() => {
157
listener.dispose();
158
callback();
159
});
160
return { dispose: () => listener.dispose() };
161
}
162
}
163
164
export interface IInlineCompletionProviderResult {
165
get didAllProvidersReturn(): boolean;
166
167
contextWithUuid: InlineCompletionContext;
168
169
cancelAndDispose(reason: InlineCompletionsDisposeReason): void;
170
171
lists: AsyncIterableProducer<InlineSuggestionList>;
172
}
173
174
function toInlineSuggestData(
175
inlineCompletion: InlineCompletion,
176
source: InlineSuggestionList,
177
defaultReplaceRange: Range,
178
textModel: ITextModel,
179
languageConfigurationService: ILanguageConfigurationService | undefined,
180
context: InlineCompletionContext,
181
requestInfo: InlineSuggestRequestInfo,
182
providerRequestInfo: InlineSuggestProviderRequestInfo,
183
): InlineSuggestData | ErrorResult {
184
185
let action: IInlineSuggestDataAction | undefined;
186
const uri = inlineCompletion.uri ? URI.revive(inlineCompletion.uri) : undefined;
187
188
if (inlineCompletion.jumpToPosition !== undefined) {
189
action = {
190
kind: 'jumpTo',
191
position: Position.lift(inlineCompletion.jumpToPosition),
192
uri,
193
};
194
} else if (inlineCompletion.insertText !== undefined) {
195
let insertText: string;
196
let snippetInfo: SnippetInfo | undefined;
197
let range = inlineCompletion.range ? Range.lift(inlineCompletion.range) : defaultReplaceRange;
198
199
if (typeof inlineCompletion.insertText === 'string') {
200
insertText = inlineCompletion.insertText;
201
202
if (languageConfigurationService && inlineCompletion.completeBracketPairs) {
203
insertText = closeBrackets(
204
insertText,
205
range.getStartPosition(),
206
textModel,
207
languageConfigurationService
208
);
209
210
// Modify range depending on if brackets are added or removed
211
const diff = insertText.length - inlineCompletion.insertText.length;
212
if (diff !== 0) {
213
range = new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn + diff);
214
}
215
}
216
217
snippetInfo = undefined;
218
} else if ('snippet' in inlineCompletion.insertText) {
219
const preBracketCompletionLength = inlineCompletion.insertText.snippet.length;
220
221
if (languageConfigurationService && inlineCompletion.completeBracketPairs) {
222
inlineCompletion.insertText.snippet = closeBrackets(
223
inlineCompletion.insertText.snippet,
224
range.getStartPosition(),
225
textModel,
226
languageConfigurationService
227
);
228
229
// Modify range depending on if brackets are added or removed
230
const diff = inlineCompletion.insertText.snippet.length - preBracketCompletionLength;
231
if (diff !== 0) {
232
range = new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn + diff);
233
}
234
}
235
236
const snippet = new SnippetParser().parse(inlineCompletion.insertText.snippet);
237
238
if (snippet.children.length === 1 && snippet.children[0] instanceof Text) {
239
insertText = snippet.children[0].value;
240
snippetInfo = undefined;
241
} else {
242
insertText = snippet.toString();
243
snippetInfo = {
244
snippet: inlineCompletion.insertText.snippet,
245
range: range
246
};
247
}
248
} else {
249
assertNever(inlineCompletion.insertText);
250
}
251
action = {
252
kind: 'edit',
253
range,
254
insertText,
255
snippetInfo,
256
uri,
257
alternativeAction: undefined,
258
};
259
} else {
260
action = undefined;
261
if (!inlineCompletion.hint) {
262
return ErrorResult.message('Inline completion has no insertText, jumpToPosition nor hint.');
263
}
264
}
265
266
return new InlineSuggestData(
267
action,
268
inlineCompletion.hint,
269
inlineCompletion.additionalTextEdits || getReadonlyEmptyArray(),
270
inlineCompletion,
271
source,
272
context,
273
inlineCompletion.isInlineEdit ?? false,
274
inlineCompletion.supportsRename ?? false,
275
requestInfo,
276
providerRequestInfo,
277
inlineCompletion.correlationId,
278
);
279
}
280
281
export type InlineSuggestSku = { type: string; plan: string };
282
283
export type InlineSuggestRequestInfo = {
284
startTime: number;
285
editorType: InlineCompletionEditorType;
286
languageId: string;
287
reason: string;
288
typingInterval: number;
289
typingIntervalCharacterCount: number;
290
availableProviders: ProviderId[];
291
sku: InlineSuggestSku | undefined;
292
};
293
294
export type InlineSuggestProviderRequestInfo = {
295
startTime: number;
296
endTime: number;
297
};
298
299
export type PartialAcceptance = {
300
characters: number;
301
count: number;
302
ratio: number;
303
};
304
305
export type RenameInfo = {
306
createdRename: boolean;
307
duration: number;
308
timedOut?: boolean;
309
droppedOtherEdits?: number;
310
droppedRenameEdits?: number;
311
};
312
313
export type InlineSuggestViewData = {
314
editorType: InlineCompletionEditorType;
315
renderData?: InlineCompletionViewData;
316
viewKind?: InlineCompletionViewKind;
317
};
318
319
export type IInlineSuggestDataAction = IInlineSuggestDataActionEdit | IInlineSuggestDataActionJumpTo;
320
321
export interface IInlineSuggestDataActionEdit {
322
kind: 'edit';
323
range: Range;
324
insertText: string;
325
snippetInfo: SnippetInfo | undefined;
326
uri: URI | undefined;
327
alternativeAction: InlineSuggestAlternativeAction | undefined;
328
}
329
330
export interface IInlineSuggestDataActionJumpTo {
331
kind: 'jumpTo';
332
position: Position;
333
uri: URI | undefined;
334
}
335
336
export class InlineSuggestData {
337
private _didShow = false;
338
private _timeUntilShown: number | undefined = undefined;
339
private _timeUntilActuallyShown: number | undefined = undefined;
340
private _showStartTime: number | undefined = undefined;
341
private _shownDuration: number = 0;
342
private _showUncollapsedStartTime: number | undefined = undefined;
343
private _showUncollapsedDuration: number = 0;
344
private _notShownReason: string | undefined = undefined;
345
346
private _viewData: InlineSuggestViewData;
347
private _didReportEndOfLife = false;
348
private _lastSetEndOfLifeReason: InlineCompletionEndOfLifeReason | undefined = undefined;
349
private _isPreceeded = false;
350
private _partiallyAcceptedCount = 0;
351
private _partiallyAcceptedSinceOriginal: PartialAcceptance = { characters: 0, ratio: 0, count: 0 };
352
private _renameInfo: RenameInfo | undefined = undefined;
353
private _editKind: InlineSuggestionEditKind | undefined = undefined;
354
355
get action(): IInlineSuggestDataAction | undefined {
356
return this._action;
357
}
358
359
constructor(
360
private _action: IInlineSuggestDataAction | undefined,
361
public readonly hint: IInlineCompletionHint | undefined,
362
public readonly additionalTextEdits: readonly ISingleEditOperation[],
363
public readonly sourceInlineCompletion: InlineCompletion,
364
public readonly source: InlineSuggestionList,
365
public readonly context: InlineCompletionContext,
366
public readonly isInlineEdit: boolean,
367
public readonly supportsRename: boolean,
368
private readonly _requestInfo: InlineSuggestRequestInfo,
369
private readonly _providerRequestInfo: InlineSuggestProviderRequestInfo,
370
private readonly _correlationId: string | undefined,
371
) {
372
this._viewData = { editorType: _requestInfo.editorType };
373
}
374
375
public get showInlineEditMenu() { return this.sourceInlineCompletion.showInlineEditMenu ?? false; }
376
377
public get partialAccepts(): PartialAcceptance { return this._partiallyAcceptedSinceOriginal; }
378
379
380
public async reportInlineEditShown(commandService: ICommandService, updatedInsertText: string, viewKind: InlineCompletionViewKind, viewData: InlineCompletionViewData, editKind: InlineSuggestionEditKind | undefined, timeWhenShown: number): Promise<void> {
381
this.updateShownDuration(viewKind);
382
383
if (this._didShow) {
384
return;
385
}
386
this.addPerformanceMarker('shown');
387
this._didShow = true;
388
this._editKind = editKind;
389
this._viewData.viewKind = viewKind;
390
this._viewData.renderData = viewData;
391
this._timeUntilShown = timeWhenShown - this._requestInfo.startTime;
392
this._timeUntilActuallyShown = Date.now() - this._requestInfo.startTime;
393
394
const editDeltaInfo = new EditDeltaInfo(viewData.lineCountModified, viewData.lineCountOriginal, viewData.characterCountModified, viewData.characterCountOriginal);
395
this.source.provider.handleItemDidShow?.(this.source.inlineSuggestions, this.sourceInlineCompletion, updatedInsertText, editDeltaInfo);
396
397
if (this.sourceInlineCompletion.shownCommand) {
398
await commandService.executeCommand(this.sourceInlineCompletion.shownCommand.id, ...(this.sourceInlineCompletion.shownCommand.arguments || []));
399
}
400
}
401
402
public reportPartialAccept(acceptedCharacters: number, info: PartialAcceptInfo, partialAcceptance: PartialAcceptance) {
403
this._partiallyAcceptedCount++;
404
this._partiallyAcceptedSinceOriginal.characters += partialAcceptance.characters;
405
this._partiallyAcceptedSinceOriginal.ratio = Math.min(this._partiallyAcceptedSinceOriginal.ratio + (1 - this._partiallyAcceptedSinceOriginal.ratio) * partialAcceptance.ratio, 1);
406
this._partiallyAcceptedSinceOriginal.count += partialAcceptance.count;
407
408
this.source.provider.handlePartialAccept?.(
409
this.source.inlineSuggestions,
410
this.sourceInlineCompletion,
411
acceptedCharacters,
412
info
413
);
414
}
415
416
/**
417
* Sends the end of life event to the provider.
418
* If no reason is provided, the last set reason is used.
419
* If no reason was set, the default reason is used.
420
*/
421
public reportEndOfLife(reason?: InlineCompletionEndOfLifeReason): void {
422
if (this._didReportEndOfLife) {
423
return;
424
}
425
this._didReportEndOfLife = true;
426
this.reportInlineEditHidden();
427
428
if (!reason) {
429
reason = this._lastSetEndOfLifeReason ?? { kind: InlineCompletionEndOfLifeReasonKind.Ignored, userTypingDisagreed: false, supersededBy: undefined };
430
}
431
432
if (reason.kind === InlineCompletionEndOfLifeReasonKind.Rejected && this.source.provider.handleRejection) {
433
this.source.provider.handleRejection(this.source.inlineSuggestions, this.sourceInlineCompletion);
434
}
435
436
if (this.source.provider.handleEndOfLifetime) {
437
const summary: LifetimeSummary = {
438
requestUuid: this.context.requestUuid,
439
correlationId: this._correlationId,
440
selectedSuggestionInfo: !!this.context.selectedSuggestionInfo,
441
partiallyAccepted: this._partiallyAcceptedCount,
442
partiallyAcceptedCountSinceOriginal: this._partiallyAcceptedSinceOriginal.count,
443
partiallyAcceptedRatioSinceOriginal: this._partiallyAcceptedSinceOriginal.ratio,
444
partiallyAcceptedCharactersSinceOriginal: this._partiallyAcceptedSinceOriginal.characters,
445
shown: this._didShow,
446
shownDuration: this._shownDuration,
447
shownDurationUncollapsed: this._showUncollapsedDuration,
448
editKind: this._editKind?.toString(),
449
preceeded: this._isPreceeded,
450
timeUntilShown: this._timeUntilShown,
451
timeUntilActuallyShown: this._timeUntilActuallyShown,
452
timeUntilProviderRequest: this._providerRequestInfo.startTime - this._requestInfo.startTime,
453
timeUntilProviderResponse: this._providerRequestInfo.endTime - this._requestInfo.startTime,
454
editorType: this._viewData.editorType,
455
languageId: this._requestInfo.languageId,
456
requestReason: this._requestInfo.reason,
457
viewKind: this._viewData.viewKind,
458
notShownReason: this._notShownReason,
459
performanceMarkers: this.performance.toString(),
460
renameCreated: this._renameInfo?.createdRename,
461
renameDuration: this._renameInfo?.duration,
462
renameTimedOut: this._renameInfo?.timedOut,
463
renameDroppedOtherEdits: this._renameInfo?.droppedOtherEdits,
464
renameDroppedRenameEdits: this._renameInfo?.droppedRenameEdits,
465
typingInterval: this._requestInfo.typingInterval,
466
typingIntervalCharacterCount: this._requestInfo.typingIntervalCharacterCount,
467
skuPlan: this._requestInfo.sku?.plan,
468
skuType: this._requestInfo.sku?.type,
469
availableProviders: this._requestInfo.availableProviders.map(p => p.toString()).join(','),
470
...this._viewData.renderData?.getData(),
471
};
472
this.source.provider.handleEndOfLifetime(this.source.inlineSuggestions, this.sourceInlineCompletion, reason, summary);
473
}
474
}
475
476
public setIsPreceeded(partialAccepts: PartialAcceptance): void {
477
this._isPreceeded = true;
478
479
if (this._partiallyAcceptedSinceOriginal.characters !== 0 || this._partiallyAcceptedSinceOriginal.ratio !== 0 || this._partiallyAcceptedSinceOriginal.count !== 0) {
480
console.warn('Expected partiallyAcceptedCountSinceOriginal to be { characters: 0, rate: 0, partialAcceptances: 0 } before setIsPreceeded.');
481
}
482
this._partiallyAcceptedSinceOriginal = partialAccepts;
483
}
484
485
public setNotShownReason(reason: string): void {
486
this._notShownReason ??= reason;
487
}
488
489
/**
490
* Sets the end of life reason, but does not send the event to the provider yet.
491
*/
492
public setEndOfLifeReason(reason: InlineCompletionEndOfLifeReason): void {
493
this.reportInlineEditHidden();
494
this._lastSetEndOfLifeReason = reason;
495
}
496
497
private updateShownDuration(viewKind: InlineCompletionViewKind) {
498
const timeNow = Date.now();
499
if (!this._showStartTime) {
500
this._showStartTime = timeNow;
501
}
502
503
const isCollapsed = viewKind === InlineCompletionViewKind.Collapsed;
504
if (!isCollapsed && this._showUncollapsedStartTime === undefined) {
505
this._showUncollapsedStartTime = timeNow;
506
}
507
508
if (isCollapsed && this._showUncollapsedStartTime !== undefined) {
509
this._showUncollapsedDuration += timeNow - this._showUncollapsedStartTime;
510
}
511
}
512
513
private reportInlineEditHidden() {
514
if (this._showStartTime === undefined) {
515
return;
516
}
517
const timeNow = Date.now();
518
this._shownDuration += timeNow - this._showStartTime;
519
this._showStartTime = undefined;
520
521
if (this._showUncollapsedStartTime === undefined) {
522
return;
523
}
524
this._showUncollapsedDuration += timeNow - this._showUncollapsedStartTime;
525
this._showUncollapsedStartTime = undefined;
526
}
527
528
public setRenameProcessingInfo(info: RenameInfo): void {
529
if (this._renameInfo) {
530
throw new BugIndicatingError('Rename info has already been set.');
531
}
532
this._renameInfo = info;
533
}
534
535
public withAction(action: IInlineSuggestDataAction): InlineSuggestData {
536
this._action = action;
537
return this;
538
}
539
540
private performance = new InlineSuggestionsPerformance();
541
public addPerformanceMarker(marker: string): void {
542
this.performance.mark(marker);
543
}
544
}
545
546
class InlineSuggestionsPerformance {
547
private markers: { name: string; timeStamp: number }[] = [];
548
constructor() {
549
this.markers.push({ name: 'start', timeStamp: Date.now() });
550
}
551
552
mark(marker: string): void {
553
this.markers.push({ name: marker, timeStamp: Date.now() });
554
}
555
556
toString(): string {
557
const deltas = [];
558
for (let i = 1; i < this.markers.length; i++) {
559
const delta = this.markers[i].timeStamp - this.markers[i - 1].timeStamp;
560
deltas.push({ [this.markers[i].name]: delta });
561
}
562
return JSON.stringify(deltas);
563
}
564
}
565
566
export interface SnippetInfo {
567
snippet: string;
568
/* Could be different than the main range */
569
range: Range;
570
}
571
572
export enum InlineCompletionEditorType {
573
TextEditor = 'textEditor',
574
DiffEditor = 'diffEditor',
575
Notebook = 'notebook',
576
}
577
578
/**
579
* A ref counted pointer to the computed `InlineCompletions` and the `InlineCompletionsProvider` that
580
* computed them.
581
*/
582
export class InlineSuggestionList {
583
private refCount = 0;
584
constructor(
585
public readonly inlineSuggestions: InlineCompletions,
586
public readonly inlineSuggestionsData: readonly InlineSuggestData[],
587
public readonly provider: InlineCompletionsProvider,
588
) { }
589
590
addRef(): void {
591
this.refCount++;
592
}
593
594
removeRef(reason: InlineCompletionsDisposeReason = { kind: 'other' }): void {
595
this.refCount--;
596
if (this.refCount === 0) {
597
for (const item of this.inlineSuggestionsData) {
598
// Fallback if it has not been called before
599
item.reportEndOfLife();
600
}
601
this.provider.disposeInlineCompletions(this.inlineSuggestions, reason);
602
}
603
}
604
}
605
606
function getDefaultRange(position: Position, model: ITextModel): Range {
607
const word = model.getWordAtPosition(position);
608
const maxColumn = model.getLineMaxColumn(position.lineNumber);
609
// By default, always replace up until the end of the current line.
610
// This default might be subject to change!
611
return word
612
? new Range(position.lineNumber, word.startColumn, position.lineNumber, maxColumn)
613
: Range.fromPositions(position, position.with(undefined, maxColumn));
614
}
615
616
function closeBrackets(text: string, position: Position, model: ITextModel, languageConfigurationService: ILanguageConfigurationService): string {
617
const currentLine = model.getLineContent(position.lineNumber);
618
const edit = StringReplacement.replace(new OffsetRange(position.column - 1, currentLine.length), text);
619
620
const proposedLineTokens = model.tokenization.tokenizeLinesAt(position.lineNumber, [edit.replace(currentLine)]);
621
const textTokens = proposedLineTokens?.[0].sliceZeroCopy(edit.getRangeAfterReplace());
622
if (!textTokens) {
623
return text;
624
}
625
626
const fixedText = fixBracketsInLine(textTokens, languageConfigurationService);
627
return fixedText;
628
}
629
630