Skip to content

Commit 315b094

Browse files
authored
Add telemetry for inline edit views (#251887)
Add telemetry to see in which view inline edits were shown
1 parent 316cb64 commit 315b094

File tree

10 files changed

+115
-22
lines changed

10 files changed

+115
-22
lines changed

src/vs/editor/common/languages.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -890,7 +890,7 @@ export interface InlineCompletionsProvider<T extends InlineCompletions = InlineC
890890
* Is called when an inline completion item is no longer being used.
891891
* Provides a reason of why it is not used anymore.
892892
*/
893-
handleEndOfLifetime?(completions: T, item: T['items'][number], reason: InlineCompletionEndOfLifeReason<T['items'][number]>): void;
893+
handleEndOfLifetime?(completions: T, item: T['items'][number], reason: InlineCompletionEndOfLifeReason<T['items'][number]>, lifetimeSummary: LifetimeSummary): void;
894894

895895
/**
896896
* Will be called when a completions list is no longer in use and can be garbage-collected.
@@ -936,6 +936,14 @@ export type InlineCompletionEndOfLifeReason<TInlineCompletion = InlineCompletion
936936
userTypingDisagreed: boolean;
937937
};
938938

939+
export type LifetimeSummary = {
940+
requestUuid: string;
941+
shown: boolean;
942+
editorType: string;
943+
viewKind: string | undefined;
944+
error: string | undefined;
945+
};
946+
939947
export interface CodeAction {
940948
title: string;
941949
command?: Command;

src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ export class InlineCompletionsModel extends Disposable {
227227
const suppressedProviderGroupIds = this._suppressedInlineCompletionGroupIds.get();
228228
const availableProviders = providers.filter(provider => !(provider.groupId && suppressedProviderGroupIds.has(provider.groupId)));
229229

230-
return this._source.fetch(availableProviders, cursorPosition, context, itemToPreserve?.identity, changeSummary.shouldDebounce, userJumpedToActiveCompletion, !!changeSummary.provider);
230+
return this._source.fetch(availableProviders, cursorPosition, context, itemToPreserve?.identity, changeSummary.shouldDebounce, userJumpedToActiveCompletion, !!changeSummary.provider, this.editorType);
231231
});
232232
this._inlineCompletionItems = derivedOpts({ owner: this }, reader => {
233233
const c = this._source.inlineCompletions.read(reader);
@@ -529,6 +529,7 @@ export class InlineCompletionsModel extends Disposable {
529529
d.getOriginalEditor().getId() === this._editor.getId() ||
530530
d.getModifiedEditor().getId() === this._editor.getId());
531531

532+
this.editorType = !!diffEditor ? 'diffEditor' : 'textEditor';
532533
this.isInDiffEditor = !!diffEditor;
533534

534535
this._register(recomputeInitiallyAndOnChange(this._fetchInlineCompletionsPromise));
@@ -538,7 +539,7 @@ export class InlineCompletionsModel extends Disposable {
538539
const item = this.inlineCompletionState.read(reader);
539540
const completion = item?.inlineCompletion;
540541
if (completion) {
541-
this.handleInlineSuggestionShown(completion);
542+
this.handleInlineSuggestionShown(completion, 'ghostText');
542543
}
543544
}));
544545

@@ -751,6 +752,8 @@ export class InlineCompletionsModel extends Disposable {
751752

752753
public readonly isInDiffEditor;
753754

755+
public readonly editorType: 'textEditor' | 'diffEditor';
756+
754757
private async _deltaSelectedInlineCompletionIndex(delta: 1 | -1): Promise<void> {
755758
await this.triggerExplicitly();
756759

@@ -1016,8 +1019,8 @@ export class InlineCompletionsModel extends Disposable {
10161019
});
10171020
}
10181021

1019-
public async handleInlineSuggestionShown(inlineCompletion: InlineSuggestionItem): Promise<void> {
1020-
await inlineCompletion.reportInlineEditShown(this._commandService);
1022+
public async handleInlineSuggestionShown(inlineCompletion: InlineSuggestionItem, viewKind: string): Promise<void> {
1023+
await inlineCompletion.reportInlineEditShown(this._commandService, viewKind);
10211024
}
10221025
}
10231026

src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export class InlineCompletionsSource extends Disposable {
116116
private readonly _loadingCount;
117117
public readonly loading;
118118

119-
public fetch(providers: InlineCompletionsProvider[], position: Position, context: InlineCompletionContextWithoutUuid, activeInlineCompletion: InlineSuggestionIdentity | undefined, withDebounce: boolean, userJumpedToActiveCompletion: IObservable<boolean>, providerhasChangedCompletion: boolean): Promise<boolean> {
119+
public fetch(providers: InlineCompletionsProvider[], position: Position, context: InlineCompletionContextWithoutUuid, activeInlineCompletion: InlineSuggestionIdentity | undefined, withDebounce: boolean, userJumpedToActiveCompletion: IObservable<boolean>, providerhasChangedCompletion: boolean, editorType: string): Promise<boolean> {
120120
const request = new UpdateRequest(position, context, this._textModel.getVersionId());
121121

122122
const target = context.selectedSuggestionInfo ? this.suggestWidgetInlineCompletions.get() : this.inlineCompletions.get();
@@ -166,6 +166,7 @@ export class InlineCompletionsSource extends Disposable {
166166
position,
167167
this._textModel,
168168
context,
169+
editorType,
169170
source.token,
170171
this._languageConfigurationService
171172
);

src/vs/editor/contrib/inlineCompletions/browser/model/inlineSuggestionItem.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ abstract class InlineSuggestionItemBase {
9999
this.source.removeRef();
100100
}
101101

102-
public reportInlineEditShown(commandService: ICommandService) {
103-
this._data.reportInlineEditShown(commandService, this.insertText);
102+
public reportInlineEditShown(commandService: ICommandService, viewKind: string) {
103+
this._data.reportInlineEditShown(commandService, this.insertText, viewKind);
104104
}
105105

106106
public reportPartialAccept(acceptedCharacters: number, info: PartialAcceptInfo) {
@@ -115,6 +115,10 @@ abstract class InlineSuggestionItemBase {
115115
this._data.setEndOfLifeReason(reason);
116116
}
117117

118+
public reportInlineEditError(reason: string): void {
119+
this._data.reportInlineEditError(reason);
120+
}
121+
118122
/**
119123
* Avoid using this method. Instead introduce getters for the needed properties.
120124
*/

src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { OffsetRange } from '../../../../common/core/ranges/offsetRange.js';
1616
import { Position } from '../../../../common/core/position.js';
1717
import { Range } from '../../../../common/core/range.js';
1818
import { TextReplacement } from '../../../../common/core/edits/textEdit.js';
19-
import { InlineCompletionEndOfLifeReason, InlineCompletionEndOfLifeReasonKind, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, InlineCompletionTriggerKind, PartialAcceptInfo, InlineCompletionsDisposeReason } from '../../../../common/languages.js';
19+
import { InlineCompletionEndOfLifeReason, InlineCompletionEndOfLifeReasonKind, InlineCompletion, InlineCompletionContext, InlineCompletions, InlineCompletionsProvider, InlineCompletionTriggerKind, PartialAcceptInfo, InlineCompletionsDisposeReason, LifetimeSummary } from '../../../../common/languages.js';
2020
import { ILanguageConfigurationService } from '../../../../common/languages/languageConfigurationRegistry.js';
2121
import { ITextModel } from '../../../../common/model.js';
2222
import { fixBracketsInLine } from '../../../../common/model/bracketPairsTextModelPart/fixBrackets.js';
@@ -34,6 +34,7 @@ export async function provideInlineCompletions(
3434
position: Position,
3535
model: ITextModel,
3636
context: InlineCompletionContextWithoutUuid,
37+
editorType: string,
3738
baseToken: CancellationToken = CancellationToken.None,
3839
languageConfigurationService?: ILanguageConfigurationService,
3940
): Promise<InlineCompletionProviderResult> {
@@ -96,7 +97,7 @@ export async function provideInlineCompletions(
9697
});
9798

9899
for (const item of result.items) {
99-
data.push(createInlineCompletionItem(item, list, defaultReplaceRange, model, languageConfigurationService, contextWithUuid));
100+
data.push(createInlineCompletionItem(item, list, defaultReplaceRange, model, languageConfigurationService, contextWithUuid, editorType));
100101
}
101102

102103
return list;
@@ -196,6 +197,7 @@ function createInlineCompletionItem(
196197
textModel: ITextModel,
197198
languageConfigurationService: ILanguageConfigurationService | undefined,
198199
context: InlineCompletionContext,
200+
editorType: string,
199201
): InlineSuggestData {
200202
let insertText: string;
201203
let snippetInfo: SnippetInfo | undefined;
@@ -269,11 +271,19 @@ function createInlineCompletionItem(
269271
source,
270272
context,
271273
inlineCompletion.isInlineEdit ?? false,
274+
editorType
272275
);
273276
}
274277

278+
export type InlineSuggestViewData = {
279+
editorType: string;
280+
viewKind?: string;
281+
error?: string;
282+
};
283+
275284
export class InlineSuggestData {
276285
private _didShow = false;
286+
private _viewData: InlineSuggestViewData;
277287
private _didReportEndOfLife = false;
278288
private _lastSetEndOfLifeReason: InlineCompletionEndOfLifeReason | undefined = undefined;
279289

@@ -288,19 +298,24 @@ export class InlineSuggestData {
288298
public readonly source: InlineSuggestionList,
289299
public readonly context: InlineCompletionContext,
290300
public readonly isInlineEdit: boolean,
291-
) { }
301+
302+
editorType: string,
303+
) {
304+
this._viewData = { editorType };
305+
}
292306

293307
public get showInlineEditMenu() { return this.sourceInlineCompletion.showInlineEditMenu ?? false; }
294308

295309
public getSingleTextEdit() {
296310
return new TextReplacement(this.range, this.insertText);
297311
}
298312

299-
public async reportInlineEditShown(commandService: ICommandService, updatedInsertText: string): Promise<void> {
313+
public async reportInlineEditShown(commandService: ICommandService, updatedInsertText: string, viewKind: string): Promise<void> {
300314
if (this._didShow) {
301315
return;
302316
}
303317
this._didShow = true;
318+
this._viewData.viewKind = viewKind;
304319

305320
this.source.provider.handleItemDidShow?.(this.source.inlineSuggestions, this.sourceInlineCompletion, updatedInsertText);
306321

@@ -338,7 +353,22 @@ export class InlineSuggestData {
338353
}
339354

340355
if (this.source.provider.handleEndOfLifetime) {
341-
this.source.provider.handleEndOfLifetime(this.source.inlineSuggestions, this.sourceInlineCompletion, reason);
356+
const summary: LifetimeSummary = {
357+
requestUuid: this.context.requestUuid,
358+
shown: this._didShow,
359+
editorType: this._viewData.editorType,
360+
viewKind: this._viewData.viewKind,
361+
error: this._viewData.error
362+
};
363+
this.source.provider.handleEndOfLifetime(this.source.inlineSuggestions, this.sourceInlineCompletion, reason, summary);
364+
}
365+
}
366+
367+
public reportInlineEditError(message: string): void {
368+
if (this._viewData.error) {
369+
this._viewData.error += `; ${message}`;
370+
} else {
371+
this._viewData.error = message;
342372
}
343373
}
344374

src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,13 @@ export class InlineEditModel implements IInlineEditModel {
5050
}
5151

5252
abort(reason: string) {
53-
console.error(reason); // TODO: add logs/telemetry
53+
console.error(reason);
54+
this.inlineEdit.inlineCompletion.reportInlineEditError(reason);
5455
this._model.stop();
5556
}
5657

57-
handleInlineEditShown() {
58-
this._model.handleInlineSuggestionShown(this.inlineEdit.inlineCompletion);
58+
handleInlineEditShown(viewKind: string) {
59+
this._model.handleInlineSuggestionShown(this.inlineEdit.inlineCompletion, viewKind);
5960
}
6061
}
6162

src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ export class InlineEditsView extends Disposable {
7676
return undefined;
7777
}
7878

79-
model.handleInlineEditShown();
80-
8179
const inlineEdit = model.inlineEdit;
8280
let mappings = RangeMapping.fromEdit(inlineEdit.edit);
8381
let newText = inlineEdit.edit.apply(inlineEdit.originalText);
@@ -89,6 +87,8 @@ export class InlineEditsView extends Disposable {
8987
return undefined;
9088
}
9189

90+
model.handleInlineEditShown(state.kind);
91+
9292
if (state.kind === 'sideBySide') {
9393
const indentationAdjustmentEdit = createReindentEdit(newText, inlineEdit.modifiedLineRange);
9494
newText = indentationAdjustmentEdit.applyToString(newText);

src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export interface IInlineEditModel {
3535
showCollapsed: IObservable<boolean>;
3636
displayLocation: InlineCompletionDisplayLocation | undefined;
3737

38-
handleInlineEditShown(): void;
38+
handleInlineEditShown(viewKind: string): void;
3939
accept(): void;
4040
jump(): void;
4141
abort(reason: string): void;

src/vs/monaco.d.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7422,7 +7422,7 @@ declare namespace monaco.languages {
74227422
* Is called when an inline completion item is no longer being used.
74237423
* Provides a reason of why it is not used anymore.
74247424
*/
7425-
handleEndOfLifetime?(completions: T, item: T['items'][number], reason: InlineCompletionEndOfLifeReason<T['items'][number]>): void;
7425+
handleEndOfLifetime?(completions: T, item: T['items'][number], reason: InlineCompletionEndOfLifeReason<T['items'][number]>, lifetimeSummary: LifetimeSummary): void;
74267426
/**
74277427
* Will be called when a completions list is no longer in use and can be garbage-collected.
74287428
*/
@@ -7463,6 +7463,14 @@ declare namespace monaco.languages {
74637463
userTypingDisagreed: boolean;
74647464
};
74657465

7466+
export type LifetimeSummary = {
7467+
requestUuid: string;
7468+
shown: boolean;
7469+
editorType: string;
7470+
viewKind: string | undefined;
7471+
error: string | undefined;
7472+
};
7473+
74667474
export interface CodeAction {
74677475
title: string;
74687476
command?: Command;

src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ import * as search from '../../contrib/search/common/search.js';
3434
import * as typeh from '../../contrib/typeHierarchy/common/typeHierarchy.js';
3535
import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js';
3636
import { ExtHostContext, ExtHostLanguageFeaturesShape, HoverWithId, ICallHierarchyItemDto, ICodeActionDto, ICodeActionProviderMetadataDto, IdentifiableInlineCompletion, IdentifiableInlineCompletions, IDocumentDropEditDto, IDocumentDropEditProviderMetadata, IDocumentFilterDto, IIndentationRuleDto, IInlayHintDto, ILanguageConfigurationDto, ILanguageWordDefinitionDto, ILinkDto, ILocationDto, ILocationLinkDto, IOnEnterRuleDto, IPasteEditDto, IPasteEditProviderMetadataDto, IRegExpDto, ISignatureHelpProviderMetadataDto, ISuggestDataDto, ISuggestDataDtoField, ISuggestResultDtoField, ITypeHierarchyItemDto, IWorkspaceSymbolDto, MainContext, MainThreadLanguageFeaturesShape } from '../common/extHost.protocol.js';
37+
import { ITelemetryService } from '../../../platform/telemetry/common/telemetry.js';
38+
import { InlineCompletionEndOfLifeReasonKind } from '../common/extHostTypes.js';
3739

3840
@extHostNamedCustomer(MainContext.MainThreadLanguageFeatures)
3941
export class MainThreadLanguageFeatures extends Disposable implements MainThreadLanguageFeaturesShape {
@@ -46,7 +48,8 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
4648
@ILanguageService private readonly _languageService: ILanguageService,
4749
@ILanguageConfigurationService private readonly _languageConfigurationService: ILanguageConfigurationService,
4850
@ILanguageFeaturesService private readonly _languageFeaturesService: ILanguageFeaturesService,
49-
@IUriIdentityService private readonly _uriIdentService: IUriIdentityService
51+
@IUriIdentityService private readonly _uriIdentService: IUriIdentityService,
52+
@ITelemetryService private readonly _telemetryService: ITelemetryService,
5053
) {
5154
super();
5255

@@ -629,7 +632,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
629632
await this._proxy.$handleInlineCompletionPartialAccept(handle, completions.pid, item.idx, acceptedCharacters, info);
630633
}
631634
},
632-
handleEndOfLifetime: async (completions, item, reason) => {
635+
handleEndOfLifetime: async (completions, item, reason, lifetimeSummary) => {
633636

634637
function mapReason<T1, T2>(reason: languages.InlineCompletionEndOfLifeReason<T1>, f: (reason: T1) => T2): languages.InlineCompletionEndOfLifeReason<T2> {
635638
if (reason.kind === languages.InlineCompletionEndOfLifeReasonKind.Ignored) {
@@ -644,6 +647,20 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread
644647
if (supportsHandleEvents) {
645648
await this._proxy.$handleInlineCompletionEndOfLifetime(handle, completions.pid, item.idx, mapReason(reason, i => ({ pid: completions.pid, idx: i.idx })));
646649
}
650+
651+
const endOfLifeSummary: InlineCompletionEndOfLifeEvent = {
652+
id: lifetimeSummary.requestUuid,
653+
shown: lifetimeSummary.shown,
654+
editorType: lifetimeSummary.editorType,
655+
viewKind: lifetimeSummary.viewKind,
656+
error: lifetimeSummary.error,
657+
superseded: reason.kind === InlineCompletionEndOfLifeReasonKind.Ignored && !!reason.supersededBy,
658+
reason: reason.kind === InlineCompletionEndOfLifeReasonKind.Accepted ? 'accepted'
659+
: reason.kind === InlineCompletionEndOfLifeReasonKind.Rejected ? 'rejected'
660+
: 'ignored'
661+
};
662+
663+
this._telemetryService.publicLog2<InlineCompletionEndOfLifeEvent, InlineCompletionsEndOfLifeClassification>('inlineCompletion.endOfLife', endOfLifeSummary);
647664
},
648665
disposeInlineCompletions: (completions: IdentifiableInlineCompletions, reason: languages.InlineCompletionsDisposeReason): void => {
649666
this._proxy.$freeInlineCompletionsList(handle, completions.pid, reason);
@@ -1265,3 +1282,24 @@ export class MainThreadDocumentRangeSemanticTokensProvider implements languages.
12651282
}
12661283
}
12671284

1285+
type InlineCompletionEndOfLifeEvent = {
1286+
id: string;
1287+
shown: boolean;
1288+
reason: 'accepted' | 'rejected' | 'ignored';
1289+
error: string | undefined;
1290+
superseded: boolean;
1291+
editorType: string;
1292+
viewKind: string | undefined;
1293+
};
1294+
1295+
type InlineCompletionsEndOfLifeClassification = {
1296+
owner: 'benibenj';
1297+
comment: 'Inline completions ended';
1298+
id: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier for the inline completion request' };
1299+
shown: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the inline completion was shown to the user' };
1300+
reason: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The reason for the inline completion ending' };
1301+
error: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The error message if the inline completion failed' };
1302+
superseded: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the inline completion was superseded by another one' };
1303+
editorType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The type of the editor where the inline completion was shown' };
1304+
viewKind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The kind of the view where the inline completion was shown' };
1305+
};

0 commit comments

Comments
 (0)