Skip to content

Commit ac4c778

Browse files
committed
fix(amazonq): Don't show inline completions when a edit is displayed
1 parent 76a1cd0 commit ac4c778

File tree

3 files changed

+181
-1
lines changed

3 files changed

+181
-1
lines changed

packages/amazonq/src/app/inline/EditRendering/displayImage.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,27 @@ export async function displaySvgDecoration(
286286
) {
287287
const originalCode = editor.document.getText()
288288

289+
// Check if a completion suggestion is currently active - if so, discard edit suggestion
290+
if (inlineCompletionProvider && inlineCompletionProvider.isCompletionActive()) {
291+
// Emit DISCARD telemetry for edit suggestion that can't be shown due to active completion
292+
const params: LogInlineCompletionSessionResultsParams = {
293+
sessionId: session.sessionId,
294+
completionSessionResult: {
295+
[item.itemId]: {
296+
seen: false,
297+
accepted: false,
298+
discarded: true,
299+
},
300+
},
301+
totalSessionDisplayTime: Date.now() - session.requestStartTime,
302+
firstCompletionDisplayLatency: session.firstCompletionDisplayLatency,
303+
isInlineEdit: true,
304+
}
305+
languageClient.sendNotification('aws/logInlineCompletionSessionResults', params)
306+
getLogger().info('Edit suggestion discarded due to active completion suggestion')
307+
return
308+
}
309+
289310
await decorationManager.displayEditSuggestion(
290311
editor,
291312
svgImage,

packages/amazonq/src/app/inline/completion.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ import {
4141
import { LineTracker } from './stateTracker/lineTracker'
4242
import { InlineTutorialAnnotation } from './tutorials/inlineTutorialAnnotation'
4343
import { TelemetryHelper } from './telemetryHelper'
44-
import { Experiments, getLogger, sleep } from 'aws-core-vscode/shared'
44+
import { Experiments, getContext, getLogger, sleep } from 'aws-core-vscode/shared'
4545
import { messageUtils } from 'aws-core-vscode/utils'
4646
import { showEdits } from './EditRendering/imageRenderer'
4747
import { ICursorUpdateRecorder } from './cursorUpdateManager'
@@ -237,6 +237,21 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem
237237
await vscode.commands.executeCommand(`aws.amazonq.checkInlineSuggestionVisibility`)
238238
}
239239

240+
/**
241+
* Check if a completion suggestion is currently active/displayed
242+
*/
243+
public isCompletionActive(): boolean {
244+
const session = this.sessionManager.getActiveSession()
245+
return session !== undefined && session.displayed && !session.suggestions.some((item) => item.isInlineEdit)
246+
}
247+
248+
/**
249+
* Check if an edit suggestion is currently active
250+
*/
251+
private isEditSuggestionActive(): boolean {
252+
return getContext('aws.amazonq.editSuggestionActive') || false
253+
}
254+
240255
// this method is automatically invoked by VS Code as user types
241256
async provideInlineCompletionItems(
242257
document: TextDocument,
@@ -435,6 +450,31 @@ ${itemLog}
435450
// the user typed characters from invoking suggestion cursor position to receiving suggestion position
436451
const typeahead = document.getText(new Range(position, editor.selection.active))
437452

453+
// Check if an edit suggestion is currently active - if so, discard completion suggestions
454+
if (this.isEditSuggestionActive()) {
455+
// Emit DISCARD telemetry for completion suggestions that can't be shown due to active edit
456+
for (const item of items) {
457+
if (!item.isInlineEdit && item.itemId) {
458+
const params: LogInlineCompletionSessionResultsParams = {
459+
sessionId: session.sessionId,
460+
completionSessionResult: {
461+
[item.itemId]: {
462+
seen: false,
463+
accepted: false,
464+
discarded: true,
465+
},
466+
},
467+
firstCompletionDisplayLatency: session.firstCompletionDisplayLatency,
468+
totalSessionDisplayTime: performance.now() - session.requestStartTime,
469+
}
470+
this.languageClient.sendNotification(this.logSessionResultMessageName, params)
471+
}
472+
}
473+
this.sessionManager.clear()
474+
logstr += `- completion suggestions discarded due to active edit suggestion`
475+
return []
476+
}
477+
438478
const itemsMatchingTypeahead = []
439479

440480
for (const item of items) {

packages/amazonq/test/unit/amazonq/apps/inline/completion.test.ts

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,125 @@ describe('InlineCompletionManager', () => {
419419
assert.deepStrictEqual((r3 as InlineCompletionItem[])[0].range?.end, new Position(1, 26))
420420
})
421421
})
422+
423+
it('should return empty array and emit telemetry when edit suggestion is active', async () => {
424+
provider = new AmazonQInlineCompletionItemProvider(
425+
languageClient,
426+
recommendationService,
427+
mockSessionManager,
428+
inlineTutorialAnnotation,
429+
documentEventListener
430+
)
431+
432+
// Stub the private method to return true (following existing pattern)
433+
sandbox.stub(provider as any, 'isEditSuggestionActive').returns(true)
434+
435+
const result = await provider.provideInlineCompletionItems(
436+
mockDocument,
437+
mockPosition,
438+
mockContext,
439+
mockToken
440+
)
441+
442+
// Should return empty array
443+
assert.deepStrictEqual(result, [])
444+
445+
// Should emit telemetry for each completion suggestion
446+
assert.strictEqual(sendNotificationStub.callCount, 2) // For both mockSuggestions
447+
448+
// Verify telemetry parameters for first call
449+
const firstCall = sendNotificationStub.getCall(0)
450+
assert.strictEqual(firstCall.args[0], 'aws/logInlineCompletionSessionResults')
451+
const sessionResult = Object.values(firstCall.args[1].completionSessionResult)[0] as any
452+
assert.strictEqual(sessionResult.seen, false)
453+
assert.strictEqual(sessionResult.accepted, false)
454+
assert.strictEqual(sessionResult.discarded, true)
455+
})
456+
457+
it('should only emit telemetry for non-inline-edit items when edit is active', async () => {
458+
provider = new AmazonQInlineCompletionItemProvider(
459+
languageClient,
460+
recommendationService,
461+
mockSessionManager,
462+
inlineTutorialAnnotation,
463+
documentEventListener
464+
)
465+
466+
sandbox.stub(provider as any, 'isEditSuggestionActive').returns(true)
467+
468+
// Mix of inline edits and completions
469+
const mixedSuggestions = [
470+
{ itemId: 'edit1', insertText: 'diff', isInlineEdit: true },
471+
{ itemId: 'completion1', insertText: 'code', isInlineEdit: false },
472+
]
473+
getActiveRecommendationStub.returns(mixedSuggestions)
474+
475+
const result = await provider.provideInlineCompletionItems(
476+
mockDocument,
477+
mockPosition,
478+
mockContext,
479+
mockToken
480+
)
481+
482+
// Should return empty array
483+
assert.deepStrictEqual(result, [])
484+
485+
// Should only emit telemetry for completion, not inline edit
486+
assert.strictEqual(sendNotificationStub.callCount, 1)
487+
const call = sendNotificationStub.getCall(0)
488+
assert(call.args[1].completionSessionResult['completion1'])
489+
assert(!call.args[1].completionSessionResult['edit1'])
490+
})
491+
492+
it('should not emit telemetry for items without itemId when edit is active', async () => {
493+
provider = new AmazonQInlineCompletionItemProvider(
494+
languageClient,
495+
recommendationService,
496+
mockSessionManager,
497+
inlineTutorialAnnotation,
498+
documentEventListener
499+
)
500+
501+
sandbox.stub(provider as any, 'isEditSuggestionActive').returns(true)
502+
503+
// Set up suggestions where some don't have itemId
504+
const suggestionsWithoutId = [
505+
{ insertText: 'code', isInlineEdit: false }, // No itemId
506+
{ itemId: 'completion1', insertText: 'code', isInlineEdit: false },
507+
]
508+
getActiveRecommendationStub.returns(suggestionsWithoutId)
509+
510+
const result = await provider.provideInlineCompletionItems(
511+
mockDocument,
512+
mockPosition,
513+
mockContext,
514+
mockToken
515+
)
516+
517+
// Should return empty array
518+
assert.deepStrictEqual(result, [])
519+
520+
// Should only emit telemetry for the item with itemId
521+
assert.strictEqual(sendNotificationStub.callCount, 1)
522+
const call = sendNotificationStub.getCall(0)
523+
assert(call.args[1].completionSessionResult['completion1'])
524+
})
525+
526+
describe('isEditSuggestionActive', () => {
527+
it('should return false when no edit suggestion is active', () => {
528+
provider = new AmazonQInlineCompletionItemProvider(
529+
languageClient,
530+
recommendationService,
531+
mockSessionManager,
532+
inlineTutorialAnnotation,
533+
documentEventListener
534+
)
535+
536+
// Since getContext returns undefined by default, this should return false
537+
const result = (provider as any).isEditSuggestionActive()
538+
assert.strictEqual(result, false)
539+
})
540+
})
422541
})
423542
})
424543
})

0 commit comments

Comments
 (0)