Skip to content

Commit 5f22745

Browse files
authored
Add shown duration to inline completions (microsoft#251918)
add shown duration
1 parent d82aa6a commit 5f22745

File tree

10 files changed

+137
-59
lines changed

10 files changed

+137
-59
lines changed

src/vs/editor/common/languages.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -939,6 +939,8 @@ export type InlineCompletionEndOfLifeReason<TInlineCompletion = InlineCompletion
939939
export type LifetimeSummary = {
940940
requestUuid: string;
941941
shown: boolean;
942+
shownDuration: number;
943+
shownDurationUncollapsed: number;
942944
editorType: string;
943945
viewKind: string | undefined;
944946
error: string | undefined;

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

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,12 @@ import { GhostText, GhostTextOrReplacement, ghostTextOrReplacementEquals, ghostT
4141
import { InlineCompletionsSource } from './inlineCompletionsSource.js';
4242
import { InlineEdit } from './inlineEdit.js';
4343
import { InlineCompletionItem, InlineEditItem, InlineSuggestionItem } from './inlineSuggestionItem.js';
44-
import { InlineCompletionContextWithoutUuid } from './provideInlineCompletions.js';
44+
import { InlineCompletionContextWithoutUuid, InlineCompletionEditorType } from './provideInlineCompletions.js';
4545
import { singleTextEditAugments, singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js';
4646
import { SuggestItemInfo } from './suggestWidgetAdapter.js';
4747
import { TextModelEditReason } from '../../../../common/textModelEditReason.js';
4848
import { ICodeEditorService } from '../../../../browser/services/codeEditorService.js';
49+
import { InlineCompletionViewKind } from '../view/inlineEdits/inlineEditsViewInterface.js';
4950

5051
export class InlineCompletionsModel extends Disposable {
5152
private readonly _source;
@@ -524,13 +525,15 @@ export class InlineCompletionsModel extends Disposable {
524525
return s.cursorAtInlineEdit.read(reader);
525526
});
526527

527-
const [diffEditor] = this._codeEditorService.listDiffEditors()
528-
.filter(d =>
529-
d.getOriginalEditor().getId() === this._editor.getId() ||
530-
d.getModifiedEditor().getId() === this._editor.getId());
528+
{ // Determine editor type
529+
const [diffEditor] = this._codeEditorService.listDiffEditors()
530+
.filter(d =>
531+
d.getOriginalEditor().getId() === this._editor.getId() ||
532+
d.getModifiedEditor().getId() === this._editor.getId());
531533

532-
this.editorType = !!diffEditor ? 'diffEditor' : 'textEditor';
533-
this.isInDiffEditor = !!diffEditor;
534+
this.editorType = !!diffEditor ? InlineCompletionEditorType.DiffEditor : InlineCompletionEditorType.TextEditor;
535+
this.isInDiffEditor = this.editorType === InlineCompletionEditorType.DiffEditor;
536+
}
534537

535538
this._register(recomputeInitiallyAndOnChange(this._fetchInlineCompletionsPromise));
536539

@@ -539,7 +542,7 @@ export class InlineCompletionsModel extends Disposable {
539542
const item = this.inlineCompletionState.read(reader);
540543
const completion = item?.inlineCompletion;
541544
if (completion) {
542-
this.handleInlineSuggestionShown(completion, 'ghostText');
545+
this.handleInlineSuggestionShown(completion, InlineCompletionViewKind.GhostText);
543546
}
544547
}));
545548

@@ -752,7 +755,7 @@ export class InlineCompletionsModel extends Disposable {
752755

753756
public readonly isInDiffEditor;
754757

755-
public readonly editorType: 'textEditor' | 'diffEditor';
758+
public readonly editorType: InlineCompletionEditorType;
756759

757760
private async _deltaSelectedInlineCompletionIndex(delta: 1 | -1): Promise<void> {
758761
await this.triggerExplicitly();
@@ -1019,7 +1022,7 @@ export class InlineCompletionsModel extends Disposable {
10191022
});
10201023
}
10211024

1022-
public async handleInlineSuggestionShown(inlineCompletion: InlineSuggestionItem, viewKind: string): Promise<void> {
1025+
public async handleInlineSuggestionShown(inlineCompletion: InlineSuggestionItem, viewKind: InlineCompletionViewKind): Promise<void> {
10231026
await inlineCompletion.reportInlineEditShown(this._commandService, viewKind);
10241027
}
10251028
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ import { IModelContentChangedEvent } from '../../../../common/textModelEvents.js
2727
import { formatRecordableLogEntry, IRecordableEditorLogEntry, IRecordableLogEntry, StructuredLogger } from '../structuredLogger.js';
2828
import { wait } from '../utils.js';
2929
import { InlineSuggestionIdentity, InlineSuggestionItem } from './inlineSuggestionItem.js';
30-
import { InlineCompletionContextWithoutUuid, InlineCompletionProviderResult, provideInlineCompletions } from './provideInlineCompletions.js';
30+
import { InlineCompletionContextWithoutUuid, InlineCompletionEditorType, InlineCompletionProviderResult, provideInlineCompletions } from './provideInlineCompletions.js';
3131

3232
export class InlineCompletionsSource extends Disposable {
3333
private static _requestId = 0;
@@ -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, editorType: string): Promise<boolean> {
119+
public fetch(providers: InlineCompletionsProvider[], position: Position, context: InlineCompletionContextWithoutUuid, activeInlineCompletion: InlineSuggestionIdentity | undefined, withDebounce: boolean, userJumpedToActiveCompletion: IObservable<boolean>, providerhasChangedCompletion: boolean, editorType: InlineCompletionEditorType): Promise<boolean> {
120120
const request = new UpdateRequest(position, context, this._textModel.getVersionId());
121121

122122
const target = context.selectedSuggestionInfo ? this.suggestWidgetInlineCompletions.get() : this.inlineCompletions.get();

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { TextModelText } from '../../../../common/model/textModelText.js';
2424
import { IDisplayLocation, InlineSuggestData, InlineSuggestionList, SnippetInfo } from './provideInlineCompletions.js';
2525
import { singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js';
2626
import { getPositionOffsetTransformerFromTextModel } from '../../../../common/core/text/getPositionOffsetTransformerFromTextModel.js';
27+
import { InlineCompletionViewKind } from '../view/inlineEdits/inlineEditsViewInterface.js';
2728

2829
export type InlineSuggestionItem = InlineEditItem | InlineCompletionItem;
2930

@@ -99,7 +100,7 @@ abstract class InlineSuggestionItemBase {
99100
this.source.removeRef();
100101
}
101102

102-
public reportInlineEditShown(commandService: ICommandService, viewKind: string) {
103+
public reportInlineEditShown(commandService: ICommandService, viewKind: InlineCompletionViewKind) {
103104
this._data.reportInlineEditShown(commandService, this.insertText, viewKind);
104105
}
105106

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

Lines changed: 55 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import { getReadonlyEmptyArray } from '../utils.js';
2626
import { groupByMap } from '../../../../../base/common/collections.js';
2727
import { DirectedGraph } from './graph.js';
2828
import { CachedFunction } from '../../../../../base/common/cache.js';
29+
import { InlineCompletionViewKind } from '../view/inlineEdits/inlineEditsViewInterface.js';
2930

3031
export type InlineCompletionContextWithoutUuid = Omit<InlineCompletionContext, 'requestUuid'>;
3132

@@ -34,7 +35,7 @@ export async function provideInlineCompletions(
3435
position: Position,
3536
model: ITextModel,
3637
context: InlineCompletionContextWithoutUuid,
37-
editorType: string,
38+
editorType: InlineCompletionEditorType,
3839
baseToken: CancellationToken = CancellationToken.None,
3940
languageConfigurationService?: ILanguageConfigurationService,
4041
): Promise<InlineCompletionProviderResult> {
@@ -197,7 +198,7 @@ function createInlineCompletionItem(
197198
textModel: ITextModel,
198199
languageConfigurationService: ILanguageConfigurationService | undefined,
199200
context: InlineCompletionContext,
200-
editorType: string,
201+
editorType: InlineCompletionEditorType,
201202
): InlineSuggestData {
202203
let insertText: string;
203204
let snippetInfo: SnippetInfo | undefined;
@@ -276,13 +277,18 @@ function createInlineCompletionItem(
276277
}
277278

278279
export type InlineSuggestViewData = {
279-
editorType: string;
280-
viewKind?: string;
280+
editorType: InlineCompletionEditorType;
281+
viewKind?: InlineCompletionViewKind;
281282
error?: string;
282283
};
283284

284285
export class InlineSuggestData {
285286
private _didShow = false;
287+
private _showStartTime: number | undefined = undefined;
288+
private _shownDuration: number = 0;
289+
private _showUncollapsedStartTime: number | undefined = undefined;
290+
private _showUncollapsedDuration: number = 0;
291+
286292
private _viewData: InlineSuggestViewData;
287293
private _didReportEndOfLife = false;
288294
private _lastSetEndOfLifeReason: InlineCompletionEndOfLifeReason | undefined = undefined;
@@ -299,7 +305,7 @@ export class InlineSuggestData {
299305
public readonly context: InlineCompletionContext,
300306
public readonly isInlineEdit: boolean,
301307

302-
editorType: string,
308+
editorType: InlineCompletionEditorType,
303309
) {
304310
this._viewData = { editorType };
305311
}
@@ -310,7 +316,9 @@ export class InlineSuggestData {
310316
return new TextReplacement(this.range, this.insertText);
311317
}
312318

313-
public async reportInlineEditShown(commandService: ICommandService, updatedInsertText: string, viewKind: string): Promise<void> {
319+
public async reportInlineEditShown(commandService: ICommandService, updatedInsertText: string, viewKind: InlineCompletionViewKind): Promise<void> {
320+
this.updateShownDuration(viewKind);
321+
314322
if (this._didShow) {
315323
return;
316324
}
@@ -343,6 +351,7 @@ export class InlineSuggestData {
343351
return;
344352
}
345353
this._didReportEndOfLife = true;
354+
this.reportInlineEditHidden();
346355

347356
if (!reason) {
348357
reason = this._lastSetEndOfLifeReason ?? { kind: InlineCompletionEndOfLifeReasonKind.Ignored, userTypingDisagreed: false, supersededBy: undefined };
@@ -356,9 +365,11 @@ export class InlineSuggestData {
356365
const summary: LifetimeSummary = {
357366
requestUuid: this.context.requestUuid,
358367
shown: this._didShow,
368+
shownDuration: this._shownDuration,
369+
shownDurationUncollapsed: this._showUncollapsedDuration,
359370
editorType: this._viewData.editorType,
360371
viewKind: this._viewData.viewKind,
361-
error: this._viewData.error
372+
error: this._viewData.error,
362373
};
363374
this.source.provider.handleEndOfLifetime(this.source.inlineSuggestions, this.sourceInlineCompletion, reason, summary);
364375
}
@@ -376,8 +387,40 @@ export class InlineSuggestData {
376387
* Sets the end of life reason, but does not send the event to the provider yet.
377388
*/
378389
public setEndOfLifeReason(reason: InlineCompletionEndOfLifeReason): void {
390+
this.reportInlineEditHidden();
379391
this._lastSetEndOfLifeReason = reason;
380392
}
393+
394+
private updateShownDuration(viewKind: InlineCompletionViewKind) {
395+
const timeNow = Date.now();
396+
if (!this._showStartTime) {
397+
this._showStartTime = timeNow;
398+
}
399+
400+
const isCollapsed = viewKind === InlineCompletionViewKind.Collapsed;
401+
if (!isCollapsed && this._showUncollapsedStartTime === undefined) {
402+
this._showUncollapsedStartTime = timeNow;
403+
}
404+
405+
if (isCollapsed && this._showUncollapsedStartTime !== undefined) {
406+
this._showUncollapsedDuration += timeNow - this._showUncollapsedStartTime;
407+
}
408+
}
409+
410+
private reportInlineEditHidden() {
411+
if (this._showStartTime === undefined) {
412+
return;
413+
}
414+
const timeNow = Date.now();
415+
this._shownDuration += timeNow - this._showStartTime;
416+
this._showStartTime = undefined;
417+
418+
if (this._showUncollapsedStartTime === undefined) {
419+
return;
420+
}
421+
this._showUncollapsedDuration += timeNow - this._showUncollapsedStartTime;
422+
this._showUncollapsedStartTime = undefined;
423+
}
381424
}
382425

383426
export interface SnippetInfo {
@@ -391,6 +434,11 @@ export interface IDisplayLocation {
391434
label: string;
392435
}
393436

437+
export enum InlineCompletionEditorType {
438+
TextEditor = 'textEditor',
439+
DiffEditor = 'diffEditor'
440+
}
441+
394442
/**
395443
* A ref counted pointer to the computed `InlineCompletions` and the `InlineCompletionsProvider` that
396444
* computed them.

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { StringText } from '../../../../../common/core/text/abstractText.js';
1414
import { Command, InlineCompletionCommand, InlineCompletionDisplayLocation } from '../../../../../common/languages.js';
1515
import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js';
1616
import { InlineCompletionItem } from '../../model/inlineSuggestionItem.js';
17-
import { IInlineEditHost, IInlineEditModel, InlineEditTabAction } from './inlineEditsViewInterface.js';
17+
import { IInlineEditHost, IInlineEditModel, InlineCompletionViewKind, InlineEditTabAction } from './inlineEditsViewInterface.js';
1818
import { InlineEditWithChanges } from './inlineEditWithChanges.js';
1919

2020
export class InlineEditModel implements IInlineEditModel {
@@ -55,7 +55,7 @@ export class InlineEditModel implements IInlineEditModel {
5555
this._model.stop();
5656
}
5757

58-
handleInlineEditShown(viewKind: string) {
58+
handleInlineEditShown(viewKind: InlineCompletionViewKind) {
5959
this._model.handleInlineSuggestionShown(this.inlineEdit.inlineCompletion, viewKind);
6060
}
6161
}

0 commit comments

Comments
 (0)