Skip to content

Commit e55ba96

Browse files
authored
track latency from request -> showing terminal completions, indicate first shown for shell type and for window (microsoft#252007)
1 parent 7ed76ef commit e55ba96

File tree

3 files changed

+104
-1
lines changed

3 files changed

+104
-1
lines changed

src/vs/workbench/contrib/terminal/common/terminalStorageKeys.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ export const enum TerminalStorageKeys {
1313
TerminalBufferState = 'terminal.integrated.bufferState',
1414
TerminalLayoutInfo = 'terminal.integrated.layoutInfo',
1515
PinnedRecentCommandsPrefix = 'terminal.pinnedRecentCommands',
16-
TerminalSuggestSize = 'terminal.integrated.suggestSize'
16+
TerminalSuggestSize = 'terminal.integrated.suggestSize',
1717
}

src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestAddon.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ export interface ISuggestController {
4545
acceptSelectedSuggestion(suggestion?: Pick<ISimpleSelectedSuggestion<TerminalCompletionItem>, 'item' | 'model'>): void;
4646
hideSuggestWidget(cancelAnyRequests: boolean, wasClosedByUser?: boolean): void;
4747
}
48+
49+
50+
let firstShownTracker: { shell: Set<TerminalShellType>; window: boolean } | undefined = undefined;
4851
export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggestController {
4952
private _terminal?: Terminal;
5053

@@ -135,6 +138,8 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
135138
private _shouldSyncWhenReady: boolean = false;
136139
private _suggestTelemetry: TerminalSuggestTelemetry | undefined;
137140

141+
private _completionRequestTimestamp: number | undefined;
142+
138143
constructor(
139144
private readonly _sessionId: string,
140145
shellType: TerminalShellType | undefined,
@@ -214,6 +219,7 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
214219
this._model?.forceRefilterAll();
215220
}
216221
}));
222+
this._register(this._extensionService.onWillStop(() => firstShownTracker = undefined));
217223
}
218224

219225
activate(xterm: Terminal): void {
@@ -333,8 +339,10 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
333339
lineContext
334340
);
335341
if (token.isCancellationRequested) {
342+
this._completionRequestTimestamp = undefined;
336343
return;
337344
}
345+
338346
this._showCompletions(model, explicitlyInvoked);
339347
}
340348

@@ -377,7 +385,16 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
377385
}
378386
this._cancellationTokenSource = new CancellationTokenSource();
379387
const token = this._cancellationTokenSource.token;
388+
389+
// Track the time when completions are requested
390+
this._completionRequestTimestamp = Date.now();
391+
380392
await this._handleCompletionProviders(this._terminal, token, explicitlyInvoked);
393+
394+
// If completions are not shown (widget not visible), reset the tracker
395+
if (!this._terminalSuggestWidgetVisibleContextKey.get()) {
396+
this._completionRequestTimestamp = undefined;
397+
}
381398
}
382399

383400
private _addPropertiesToInlineCompletionItem(completions: ITerminalCompletion[]): void {
@@ -679,6 +696,16 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
679696
if (!cursorPosition) {
680697
return;
681698
}
699+
// Track the time when completions are shown for the first time
700+
if (this._completionRequestTimestamp !== undefined) {
701+
const completionLatency = Date.now() - this._completionRequestTimestamp;
702+
if (this._suggestTelemetry && this.shellType) {
703+
const firstShown = this.getFirstShown(this.shellType);
704+
this.updateShown();
705+
this._suggestTelemetry.logCompletionLatency(this._sessionId, completionLatency, firstShown);
706+
}
707+
this._completionRequestTimestamp = undefined;
708+
}
682709
suggestWidget.showSuggestions(0, false, !explicitlyInvoked, cursorPosition);
683710
}
684711

@@ -853,6 +880,37 @@ export class SuggestAddon extends Disposable implements ITerminalAddon, ISuggest
853880
this._leadingLineContent = undefined;
854881
this._suggestWidget?.hide();
855882
}
883+
884+
getFirstShown(shellType: TerminalShellType): { window: boolean; shell: boolean } {
885+
if (!firstShownTracker) {
886+
firstShownTracker = {
887+
window: true,
888+
shell: new Set([shellType])
889+
};
890+
return { window: true, shell: true };
891+
}
892+
893+
const isFirstForWindow = firstShownTracker.window;
894+
const isFirstForShell = !firstShownTracker.shell.has(shellType);
895+
896+
if (isFirstForWindow || isFirstForShell) {
897+
this.updateShown();
898+
}
899+
900+
return {
901+
window: isFirstForWindow,
902+
shell: isFirstForShell
903+
};
904+
}
905+
906+
updateShown(): void {
907+
if (!this.shellType || !firstShownTracker) {
908+
return;
909+
}
910+
911+
firstShownTracker.window = false;
912+
firstShownTracker.shell.add(this.shellType);
913+
}
856914
}
857915

858916
class PersistedWidgetSize {

src/vs/workbench/contrib/terminalContrib/suggest/browser/terminalSuggestTelemetry.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,51 @@ export class TerminalSuggestTelemetry extends Disposable {
4848
this._acceptedCompletions = this._acceptedCompletions || [];
4949
this._acceptedCompletions.push({ label: typeof completion.label === 'string' ? completion.label : completion.label.label, kind: this._kindMap.get(completion.kind!), sessionId });
5050
}
51+
52+
/**
53+
* Logs the latency (ms) from completion request to completions shown.
54+
* @param sessionId The terminal session ID
55+
* @param latency The measured latency in ms
56+
* @param firstShownFor Object indicating if completions have been shown for window/shell
57+
*/
58+
logCompletionLatency(sessionId: string, latency: number, firstShownFor: { window: boolean; shell: boolean }): void {
59+
this._telemetryService.publicLog2<{
60+
sessionId: string;
61+
latency: number;
62+
firstWindow: boolean;
63+
firstShell: boolean;
64+
}, {
65+
owner: 'meganrogge';
66+
comment: 'Latency in ms from terminal completion request to completions shown.';
67+
sessionId: {
68+
classification: 'SystemMetaData';
69+
purpose: 'FeatureInsight';
70+
comment: 'The session ID of the terminal session.';
71+
};
72+
latency: {
73+
classification: 'SystemMetaData';
74+
purpose: 'PerformanceAndHealth';
75+
comment: 'The latency in milliseconds.';
76+
};
77+
firstWindow: {
78+
classification: 'SystemMetaData';
79+
purpose: 'FeatureInsight';
80+
comment: 'Whether this is the first ever showing of completions in the window.';
81+
};
82+
firstShell: {
83+
classification: 'SystemMetaData';
84+
purpose: 'FeatureInsight';
85+
comment: 'Whether this is the first ever showing of completions in the shell.';
86+
};
87+
}>('terminal.suggest.completionLatency', {
88+
sessionId,
89+
latency,
90+
firstWindow: firstShownFor.window,
91+
firstShell: firstShownFor.shell
92+
});
93+
}
94+
95+
5196
private _sendTelemetryInfo(fromInterrupt?: boolean, exitCode?: number): void {
5297
const commandLine = this._promptInputModel?.value;
5398
for (const completion of this._acceptedCompletions || []) {

0 commit comments

Comments
 (0)