Skip to content

Commit 02bf0cc

Browse files
authored
[CodeWhisperer] Add missing userDecision events for conflicting popup case (#3265)
1. When CodeWhisperer is invoked but IntelliSense popup is opened after, we are not showing recommendations due to conflicting popups in this case previously we rely on disposing the popup from the caller processCodeWhispererUI(), but the popupListener to send userDecision event has not yet been registered to the popup. So we send the event manually just like the caret moving backward case.
1 parent bb17b75 commit 02bf0cc

File tree

2 files changed

+107
-26
lines changed

2 files changed

+107
-26
lines changed

jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -339,16 +339,12 @@ class CodeWhispererService {
339339

340340
if (CodeWhispererPopupManager.getInstance().hasConflictingPopups(requestContext.editor)) {
341341
LOG.debug { "Detect conflicting popup window with CodeWhisperer popup, not showing CodeWhisperer popup" }
342+
sendDiscardedUserDecisionEventForAll(requestContext, responseContext, recommendations)
342343
return null
343344
}
344345
if (caretMovement == CaretMovement.MOVE_BACKWARD) {
345346
LOG.debug { "Caret moved backward, discarding all of the recommendations. Request ID: $requestId" }
346-
val detailContexts = recommendations.map { DetailContext("", it, it, true) }
347-
val recommendationContext = RecommendationContext(detailContexts, "", "", VisualPosition(0, 0))
348-
349-
CodeWhispererTelemetryService.getInstance().sendUserDecisionEventForAll(
350-
requestContext, responseContext, recommendationContext, SessionContext(), false
351-
)
347+
sendDiscardedUserDecisionEventForAll(requestContext, responseContext, recommendations)
352348
return null
353349
}
354350
val userInputOriginal = CodeWhispererEditorManager.getInstance().getUserInputSinceInvocation(
@@ -432,6 +428,19 @@ class CodeWhispererService {
432428
CodeWhispererPopupManager.getInstance().changeStates(states, 0, "", true, recommendationAdded)
433429
}
434430

431+
private fun sendDiscardedUserDecisionEventForAll(
432+
requestContext: RequestContext,
433+
responseContext: ResponseContext,
434+
recommendations: List<Recommendation>
435+
) {
436+
val detailContexts = recommendations.map { DetailContext("", it, it, true) }
437+
val recommendationContext = RecommendationContext(detailContexts, "", "", VisualPosition(0, 0))
438+
439+
CodeWhispererTelemetryService.getInstance().sendUserDecisionEventForAll(
440+
requestContext, responseContext, recommendationContext, SessionContext(), false
441+
)
442+
}
443+
435444
fun getRequestContext(
436445
triggerTypeInfo: TriggerTypeInfo,
437446
editor: Editor,

jetbrains-core/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTelemetryTest.kt

Lines changed: 92 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
108108
argumentCaptor<MetricEvent>().apply {
109109
verify(batcher, atLeastOnce()).enqueue(capture())
110110
assertEventsContainsFieldsAndCount(
111-
allValues, serviceInvocation, 1,
111+
allValues,
112+
serviceInvocation,
113+
1,
112114
"result" to Result.Failed.toString()
113115
)
114116
}
@@ -156,15 +158,21 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
156158
verify(batcher, atLeast(2 + count)).enqueue(capture())
157159
assertEventsContainsFieldsAndCount(allValues, serviceInvocation, 1)
158160
assertEventsContainsFieldsAndCount(
159-
allValues, userModification, 1,
161+
allValues,
162+
userModification,
163+
1,
160164
"codewhispererSessionId" to testSessionId
161165
)
162166
assertEventsContainsFieldsAndCount(
163-
allValues, userDecision, 1,
167+
allValues,
168+
userDecision,
169+
1,
164170
codewhispererSuggestionState to CodewhispererSuggestionState.Accept.toString()
165171
)
166172
assertEventsContainsFieldsAndCount(
167-
allValues, userDecision, count - 1,
173+
allValues,
174+
userDecision,
175+
count - 1,
168176
codewhispererSuggestionState to CodewhispererSuggestionState.Unseen.toString()
169177
)
170178
}
@@ -185,15 +193,21 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
185193
argumentCaptor<MetricEvent>().apply {
186194
verify(batcher, atLeast(1 + count)).enqueue(capture())
187195
assertEventsContainsFieldsAndCount(
188-
allValues, serviceInvocation, 1,
196+
allValues,
197+
serviceInvocation,
198+
1,
189199
"result" to Result.Succeeded.toString()
190200
)
191201
assertEventsContainsFieldsAndCount(
192-
allValues, userDecision, 1,
202+
allValues,
203+
userDecision,
204+
1,
193205
codewhispererSuggestionState to CodewhispererSuggestionState.Reject.toString()
194206
)
195207
assertEventsContainsFieldsAndCount(
196-
allValues, userDecision, count - 1,
208+
allValues,
209+
userDecision,
210+
count - 1,
197211
codewhispererSuggestionState to CodewhispererSuggestionState.Unseen.toString()
198212
)
199213
}
@@ -206,7 +220,9 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
206220
argumentCaptor<MetricEvent>().apply {
207221
verify(batcher, atLeastOnce()).enqueue(capture())
208222
assertEventsContainsFieldsAndCount(
209-
allValues, serviceInvocation, 1,
223+
allValues,
224+
serviceInvocation,
225+
1,
210226
"result" to Result.Succeeded.toString()
211227
)
212228
}
@@ -232,11 +248,15 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
232248
argumentCaptor<MetricEvent>().apply {
233249
verify(batcher, atLeast(1 + count)).enqueue(capture())
234250
assertEventsContainsFieldsAndCount(
235-
allValues, serviceInvocation, 1,
251+
allValues,
252+
serviceInvocation,
253+
1,
236254
"result" to Result.Succeeded.toString()
237255
)
238256
assertEventsContainsFieldsAndCount(
239-
allValues, userDecision, count,
257+
allValues,
258+
userDecision,
259+
count,
240260
codewhispererSuggestionState to CodewhispererSuggestionState.Discard.toString()
241261
)
242262
}
@@ -268,11 +288,15 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
268288
argumentCaptor<MetricEvent>().apply {
269289
verify(batcher, atLeast(1 + prefixNotMatchCount)).enqueue(capture())
270290
assertEventsContainsFieldsAndCount(
271-
allValues, serviceInvocation, 1,
291+
allValues,
292+
serviceInvocation,
293+
1,
272294
"result" to Result.Succeeded.toString()
273295
)
274296
assertEventsContainsFieldsAndCount(
275-
allValues, userDecision, prefixNotMatchCount,
297+
allValues,
298+
userDecision,
299+
prefixNotMatchCount,
276300
codewhispererSuggestionState to CodewhispererSuggestionState.Discard.toString()
277301
)
278302
}
@@ -293,7 +317,9 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
293317
argumentCaptor<MetricEvent>().apply {
294318
verify(batcher, atLeastOnce()).enqueue(capture())
295319
assertEventsContainsFieldsAndCount(
296-
allValues, serviceInvocation, 1,
320+
allValues,
321+
serviceInvocation,
322+
1,
297323
"codewhispererRequestId" to "",
298324
"result" to Result.Failed.toString()
299325
)
@@ -315,7 +341,9 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
315341
argumentCaptor<MetricEvent>().apply {
316342
verify(batcher, atLeastOnce()).enqueue(capture())
317343
assertEventsContainsFieldsAndCount(
318-
allValues, serviceInvocation, 1,
344+
allValues,
345+
serviceInvocation,
346+
1,
319347
"codewhispererRequestId" to testRequestIdForCodeWhispererException,
320348
"result" to Result.Failed.toString()
321349
)
@@ -332,7 +360,9 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
332360
argumentCaptor<MetricEvent>().apply {
333361
verify(batcher, atLeast(1 + pythonResponse.recommendations().size)).enqueue(capture())
334362
assertEventsContainsFieldsAndCount(
335-
allValues, userDecision, pythonResponse.recommendations().size,
363+
allValues,
364+
userDecision,
365+
pythonResponse.recommendations().size,
336366
"codewhispererSuggestionState" to CodewhispererSuggestionState.Filter.toString()
337367
)
338368
}
@@ -345,7 +375,9 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
345375
verify(batcher, atLeastOnce()).enqueue(capture())
346376
pythonResponse.recommendations().forEach {
347377
assertEventsContainsFieldsAndCount(
348-
allValues, userDecision, 1,
378+
allValues,
379+
userDecision,
380+
1,
349381
"codewhispererSuggestionReferences" to Gson().toJson(it.references().map { ref -> ref.licenseName() }.toSet()),
350382
"codewhispererSuggestionReferenceCount" to it.references().size.toString(),
351383
atLeast = true
@@ -363,7 +395,9 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
363395
val metricCaptor = argumentCaptor<MetricEvent>()
364396
verify(batcher, atLeastOnce()).enqueue(metricCaptor.capture())
365397
assertEventsContainsFieldsAndCount(
366-
metricCaptor.allValues, serviceInvocation, 1,
398+
metricCaptor.allValues,
399+
serviceInvocation,
400+
1,
367401
"codewhispererSessionId" to states.responseContext.sessionId,
368402
"codewhispererRequestId" to states.recommendationContext.details[0].requestId
369403
)
@@ -379,12 +413,46 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
379413
val metricCaptor = argumentCaptor<MetricEvent>()
380414
verify(batcher, atLeastOnce()).enqueue(metricCaptor.capture())
381415
assertEventsContainsFieldsAndCount(
382-
metricCaptor.allValues, userDecision, states.recommendationContext.details.size,
416+
metricCaptor.allValues,
417+
userDecision,
418+
states.recommendationContext.details.size,
383419
"codewhispererSessionId" to states.responseContext.sessionId,
384420
"codewhispererRequestId" to states.recommendationContext.details[0].requestId,
385421
)
386422
}
387423

424+
@Test
425+
fun `test showing IntelliSense after triggering CodeWhisperer will send userDecision events of state Discard`() {
426+
val codewhispererServiceSpy = spy(codewhispererService)
427+
codewhispererServiceSpy.stub {
428+
onGeneric {
429+
canDoInvocation(any(), any())
430+
} doAnswer {
431+
true
432+
}
433+
}
434+
ApplicationManager.getApplication().replaceService(CodeWhispererService::class.java, codewhispererServiceSpy, disposableRule.disposable)
435+
popupManagerSpy.stub {
436+
onGeneric {
437+
hasConflictingPopups(any())
438+
} doAnswer {
439+
true
440+
}
441+
}
442+
invokeCodeWhispererService()
443+
444+
runInEdtAndWait {
445+
val metricCaptor = argumentCaptor<MetricEvent>()
446+
verify(batcher, atLeastOnce()).enqueue(metricCaptor.capture())
447+
assertEventsContainsFieldsAndCount(
448+
metricCaptor.allValues,
449+
userDecision,
450+
pythonResponse.recommendations().size,
451+
codewhispererSuggestionState to CodewhispererSuggestionState.Discard.toString(),
452+
)
453+
}
454+
}
455+
388456
@Test
389457
fun `test codePercentage metric is correct - 1`() {
390458
val project = projectRule.project
@@ -540,7 +608,9 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
540608
val metricCaptor = argumentCaptor<MetricEvent>()
541609
verify(batcher, atLeastOnce()).enqueue(metricCaptor.capture())
542610
assertEventsContainsFieldsAndCount(
543-
metricCaptor.allValues, userDecision, 1,
611+
metricCaptor.allValues,
612+
userDecision,
613+
1,
544614
"codewhispererSuggestionState" to CodewhispererSuggestionState.Empty.toString()
545615
)
546616
}
@@ -611,7 +681,9 @@ class CodeWhispererTelemetryTest : CodeWhispererTestBase() {
611681
val metricCaptor = argumentCaptor<MetricEvent>()
612682
verify(batcher, atLeastOnce()).enqueue(metricCaptor.capture())
613683
assertEventsContainsFieldsAndCount(
614-
metricCaptor.allValues, userDecision, numOfEmptyRecommendations,
684+
metricCaptor.allValues,
685+
userDecision,
686+
numOfEmptyRecommendations,
615687
"codewhispererSuggestionState" to CodewhispererSuggestionState.Empty.toString()
616688
)
617689
}

0 commit comments

Comments
 (0)