@@ -26,8 +26,21 @@ import { IDecoration, Terminal } from 'xterm';
26
26
// Importing types is safe in any layer
27
27
// eslint-disable-next-line local/code-import-patterns
28
28
import type { ITerminalAddon } from 'xterm-headless' ;
29
-
30
-
29
+ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry' ;
30
+ import { ILogService } from 'vs/platform/log/common/log' ;
31
+ const quickFixTelemetryTitle = 'terminal/quick-fix' ;
32
+ type QuickFixResultTelemetryEvent = {
33
+ id : string ;
34
+ fixesShown : boolean ;
35
+ ranQuickFixCommand ?: boolean ;
36
+ } ;
37
+ type QuickFixClassification = {
38
+ owner : 'meganrogge' ;
39
+ id : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'The quick fix ID' } ;
40
+ fixesShown : { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'Whether the fixes were shown by the user' } ;
41
+ ranQuickFixCommand ?: { classification : 'SystemMetaData' ; purpose : 'FeatureInsight' ; comment : 'If the command that was executed matched a quick fix suggested one. Undefined if no command is expected.' } ;
42
+ comment : 'Terminal quick fixes' ;
43
+ } ;
31
44
const quickFixSelectors = [ DecorationSelector . QuickFix , DecorationSelector . LightBulb , DecorationSelector . Codicon , DecorationSelector . CommandDecoration , DecorationSelector . XtermDecoration ] ;
32
45
export interface ITerminalQuickFix {
33
46
showMenu ( ) : void ;
@@ -56,13 +69,18 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon,
56
69
57
70
private readonly _terminalDecorationHoverService : TerminalDecorationHoverManager ;
58
71
72
+ private _fixesShown : boolean = false ;
73
+ private _expectedCommands : string [ ] | undefined ;
74
+
59
75
constructor ( private readonly _capabilities : ITerminalCapabilityStore ,
60
76
@IContextMenuService private readonly _contextMenuService : IContextMenuService ,
61
77
@IConfigurationService private readonly _configurationService : IConfigurationService ,
62
78
@IInstantiationService instantiationService : IInstantiationService ,
63
79
@IAudioCueService private readonly _audioCueService : IAudioCueService ,
64
80
@IKeybindingService private readonly _keybindingService : IKeybindingService ,
65
- @IOpenerService private readonly _openerService : IOpenerService
81
+ @IOpenerService private readonly _openerService : IOpenerService ,
82
+ @ITelemetryService private readonly _telemetryService : ITelemetryService ,
83
+ @ILogService private readonly _logService : ILogService
66
84
) {
67
85
super ( ) ;
68
86
const commandDetectionCapability = this . _capabilities . get ( TerminalCapability . CommandDetection ) ;
@@ -83,6 +101,7 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon,
83
101
}
84
102
85
103
showMenu ( ) : void {
104
+ this . _fixesShown = true ;
86
105
this . _decoration ?. element ?. click ( ) ;
87
106
}
88
107
@@ -99,7 +118,25 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon,
99
118
if ( ! terminal || ! commandDetection ) {
100
119
return ;
101
120
}
102
- this . _register ( commandDetection . onCommandFinished ( command => this . _resolveQuickFixes ( command ) ) ) ;
121
+ this . _register ( commandDetection . onCommandFinished ( command => {
122
+ if ( this . _expectedCommands ) {
123
+ const ranQuickFixCommand = this . _expectedCommands . includes ( command . command ) ;
124
+ this . _logService . debug ( quickFixTelemetryTitle , {
125
+ id : this . _expectedCommands . join ( ' ' ) ,
126
+ fixesShown : this . _fixesShown ,
127
+ ranQuickFixCommand
128
+ } ) ;
129
+ this . _telemetryService ?. publicLog2 < QuickFixResultTelemetryEvent , QuickFixClassification > ( quickFixTelemetryTitle , {
130
+ id : this . _expectedCommands . join ( ' ' ) ,
131
+ fixesShown : this . _fixesShown ,
132
+ ranQuickFixCommand
133
+ } ) ;
134
+ this . _expectedCommands = undefined ;
135
+ }
136
+ this . _resolveQuickFixes ( command ) ;
137
+ this . _fixesShown = false ;
138
+ } ) ) ;
139
+
103
140
// The buffer is not ready by the time command finish
104
141
// is called. Add the decoration on command start if there are corresponding quick fixes
105
142
this . _register ( commandDetection . onCommandStarted ( ( ) => {
@@ -120,9 +157,24 @@ export class TerminalQuickFixAddon extends Disposable implements ITerminalAddon,
120
157
if ( ! result ) {
121
158
return ;
122
159
}
123
- const { fixes, onDidRunQuickFix } = result ;
160
+ const { fixes, onDidRunQuickFix, expectedCommands } = result ;
161
+ this . _expectedCommands = expectedCommands ;
124
162
this . _quickFixes = fixes ;
125
- onDidRunQuickFix ( ( ) => this . _disposeQuickFix ( ) ) ;
163
+ this . _register ( onDidRunQuickFix ( ( id ) => {
164
+ const ranQuickFixCommand = ( this . _expectedCommands ?. includes ( command . command ) || false ) ;
165
+ this . _logService . debug ( quickFixTelemetryTitle , {
166
+ id,
167
+ fixesShown : this . _fixesShown ,
168
+ ranQuickFixCommand
169
+ } ) ;
170
+ this . _telemetryService ?. publicLog2 < QuickFixResultTelemetryEvent , QuickFixClassification > ( quickFixTelemetryTitle , {
171
+ id,
172
+ fixesShown : this . _fixesShown ,
173
+ ranQuickFixCommand
174
+ } ) ;
175
+ this . _disposeQuickFix ( ) ;
176
+ this . _fixesShown = false ;
177
+ } ) ) ;
126
178
}
127
179
128
180
private _disposeQuickFix ( ) : void {
@@ -177,11 +229,12 @@ export function getQuickFixesForCommand(
177
229
quickFixOptions : Map < string , ITerminalQuickFixOptions [ ] > ,
178
230
openerService : IOpenerService ,
179
231
onDidRequestRerunCommand ?: Emitter < { command : string ; addNewLine ?: boolean } >
180
- ) : { fixes : IAction [ ] ; onDidRunQuickFix : Event < void > } | undefined {
181
- const onDidRunQuickFixEmitter = new Emitter < void > ( ) ;
232
+ ) : { fixes : IAction [ ] ; onDidRunQuickFix : Event < string > ; expectedCommands ?: string [ ] } | undefined {
233
+ const onDidRunQuickFixEmitter = new Emitter < string > ( ) ;
182
234
const onDidRunQuickFix = onDidRunQuickFixEmitter . event ;
183
235
const fixes : IAction [ ] = [ ] ;
184
236
const newCommand = command . command ;
237
+ const expectedCommands = [ ] ;
185
238
for ( const options of quickFixOptions . values ( ) ) {
186
239
for ( const option of options ) {
187
240
if ( option . exitStatus !== undefined && option . exitStatus !== ( command . exitCode === 0 ) ) {
@@ -196,6 +249,7 @@ export function getQuickFixesForCommand(
196
249
if ( outputMatcher ) {
197
250
outputMatch = command . getOutputMatch ( outputMatcher ) ;
198
251
}
252
+ const id = option . id ;
199
253
const quickFixes = option . getQuickFixes ( { commandLineMatch, outputMatch } , command ) ;
200
254
if ( quickFixes ) {
201
255
for ( const quickFix of asArray ( quickFixes ) ) {
@@ -218,6 +272,7 @@ export function getQuickFixesForCommand(
218
272
tooltip : label ,
219
273
command : quickFix . command
220
274
} as IAction ;
275
+ expectedCommands . push ( quickFix . command ) ;
221
276
break ;
222
277
}
223
278
case 'opener' : {
@@ -231,7 +286,7 @@ export function getQuickFixesForCommand(
231
286
openerService . open ( quickFix . uri ) ;
232
287
// since no command gets run here, need to
233
288
// clear the decoration and quick fix
234
- onDidRunQuickFixEmitter . fire ( ) ;
289
+ onDidRunQuickFixEmitter . fire ( id ) ;
235
290
} ,
236
291
tooltip : label ,
237
292
uri : quickFix . uri
@@ -245,7 +300,10 @@ export function getQuickFixesForCommand(
245
300
label : quickFix . label ,
246
301
class : quickFix . class ,
247
302
enabled : quickFix . enabled ,
248
- run : ( ) => quickFix . run ( ) ,
303
+ run : ( ) => {
304
+ quickFix . run ( ) ;
305
+ onDidRunQuickFixEmitter . fire ( id ) ;
306
+ } ,
249
307
tooltip : quickFix . tooltip
250
308
} ;
251
309
}
@@ -256,7 +314,7 @@ export function getQuickFixesForCommand(
256
314
}
257
315
}
258
316
}
259
- return fixes . length > 0 ? { fixes, onDidRunQuickFix } : undefined ;
317
+ return fixes . length > 0 ? { fixes, onDidRunQuickFix, expectedCommands } : undefined ;
260
318
}
261
319
262
320
0 commit comments