Skip to content

Commit 8afcc81

Browse files
authored
feat(amazonq): handle reference for inline with language server (aws#6837)
## Problem missing log references for inline with language server ## Solution add them --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
1 parent 7ae12df commit 8afcc81

File tree

2 files changed

+190
-10
lines changed

2 files changed

+190
-10
lines changed

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

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,22 @@ import {
1414
commands,
1515
languages,
1616
Disposable,
17+
window,
18+
TextEditor,
1719
} from 'vscode'
1820
import { LanguageClient } from 'vscode-languageclient'
19-
import { LogInlineCompletionSessionResultsParams } from '@aws/language-server-runtimes/protocol'
21+
import {
22+
InlineCompletionItemWithReferences,
23+
LogInlineCompletionSessionResultsParams,
24+
} from '@aws/language-server-runtimes/protocol'
2025
import { SessionManager } from './sessionManager'
2126
import { RecommendationService } from './recommendationService'
22-
import { CodeWhispererConstants } from 'aws-core-vscode/codewhisperer'
27+
import {
28+
CodeWhispererConstants,
29+
ReferenceHoverProvider,
30+
ReferenceInlineProvider,
31+
ReferenceLogViewProvider,
32+
} from 'aws-core-vscode/codewhisperer'
2333

2434
export class InlineCompletionManager implements Disposable {
2535
private disposable: Disposable
@@ -53,15 +63,16 @@ export class InlineCompletionManager implements Disposable {
5363
public registerInlineCompletion() {
5464
const onInlineAcceptance = async (
5565
sessionId: string,
56-
itemId: string,
66+
item: InlineCompletionItemWithReferences,
67+
editor: TextEditor,
5768
requestStartTime: number,
5869
firstCompletionDisplayLatency?: number
5970
) => {
6071
// TODO: also log the seen state for other suggestions in session
6172
const params: LogInlineCompletionSessionResultsParams = {
6273
sessionId: sessionId,
6374
completionSessionResult: {
64-
[itemId]: {
75+
[item.itemId]: {
6576
seen: true,
6677
accepted: true,
6778
discarded: false,
@@ -76,6 +87,15 @@ export class InlineCompletionManager implements Disposable {
7687
CodeWhispererConstants.platformLanguageIds,
7788
this.inlineCompletionProvider
7889
)
90+
if (item.references && item.references.length) {
91+
const referenceLog = ReferenceLogViewProvider.getReferenceLog(
92+
item.insertText as string,
93+
item.references,
94+
editor
95+
)
96+
ReferenceLogViewProvider.instance.addReferenceLog(referenceLog)
97+
ReferenceHoverProvider.instance.addCodeReferences(item.insertText as string, item.references)
98+
}
7999
}
80100
commands.registerCommand('aws.amazonq.acceptInline', onInlineAcceptance)
81101

@@ -170,17 +190,24 @@ export class AmazonQInlineCompletionItemProvider implements InlineCompletionItem
170190
if (!session || !items.length) {
171191
return []
172192
}
193+
const editor = window.activeTextEditor
173194
for (const item of items) {
174195
item.command = {
175196
command: 'aws.amazonq.acceptInline',
176197
title: 'On acceptance',
177198
arguments: [
178199
session.sessionId,
179-
item.itemId,
200+
item,
201+
editor,
180202
session.requestStartTime,
181203
session.firstCompletionDisplayLatency,
182204
],
183205
}
206+
ReferenceInlineProvider.instance.setInlineReference(
207+
position.line,
208+
item.insertText as string,
209+
item.references
210+
)
184211
}
185212
return items as InlineCompletionItem[]
186213
}

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

Lines changed: 158 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,18 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55
import sinon from 'sinon'
6-
import { commands, languages } from 'vscode'
6+
import { CancellationToken, commands, languages, Position } from 'vscode'
77
import assert from 'assert'
88
import { LanguageClient } from 'vscode-languageclient'
9-
import { InlineCompletionManager } from '../../../../../src/app/inline/completion'
9+
import { AmazonQInlineCompletionItemProvider, InlineCompletionManager } from '../../../../../src/app/inline/completion'
10+
import { RecommendationService } from '../../../../../src/app/inline/recommendationService'
11+
import { SessionManager } from '../../../../../src/app/inline/sessionManager'
12+
import { createMockDocument, createMockTextEditor } from 'aws-core-vscode/test'
13+
import {
14+
ReferenceHoverProvider,
15+
ReferenceInlineProvider,
16+
ReferenceLogViewProvider,
17+
} from 'aws-core-vscode/codewhisperer'
1018

1119
describe('InlineCompletionManager', () => {
1220
let manager: InlineCompletionManager
@@ -19,6 +27,32 @@ describe('InlineCompletionManager', () => {
1927
let sandbox: sinon.SinonSandbox
2028
let getActiveSessionStub: sinon.SinonStub
2129
let getActiveRecommendationStub: sinon.SinonStub
30+
let logReferenceStub: sinon.SinonStub
31+
let getReferenceStub: sinon.SinonStub
32+
let hoverReferenceStub: sinon.SinonStub
33+
const mockDocument = createMockDocument()
34+
const mockEditor = createMockTextEditor()
35+
const mockPosition = { line: 0, character: 0 } as Position
36+
const mockContext = { triggerKind: 1, selectedCompletionInfo: undefined }
37+
const mockToken = { isCancellationRequested: false } as CancellationToken
38+
const fakeReferences = [
39+
{
40+
message: '',
41+
licenseName: 'TEST_LICENSE',
42+
repository: 'TEST_REPO',
43+
recommendationContentSpan: {
44+
start: 0,
45+
end: 10,
46+
},
47+
},
48+
]
49+
const mockSuggestions = [
50+
{
51+
itemId: 'test-item',
52+
insertText: 'test',
53+
references: fakeReferences,
54+
},
55+
]
2256

2357
beforeEach(() => {
2458
sandbox = sinon.createSandbox()
@@ -41,6 +75,9 @@ describe('InlineCompletionManager', () => {
4175
manager = new InlineCompletionManager(languageClient)
4276
getActiveSessionStub = sandbox.stub(manager['sessionManager'], 'getActiveSession')
4377
getActiveRecommendationStub = sandbox.stub(manager['sessionManager'], 'getActiveRecommendation')
78+
getReferenceStub = sandbox.stub(ReferenceLogViewProvider, 'getReferenceLog')
79+
logReferenceStub = sandbox.stub(ReferenceLogViewProvider.instance, 'addReferenceLog')
80+
hoverReferenceStub = sandbox.stub(ReferenceHoverProvider.instance, 'addCodeReferences')
4481
})
4582

4683
afterEach(() => {
@@ -65,11 +102,16 @@ describe('InlineCompletionManager', () => {
65102
?.find((call) => call.args[0] === 'aws.amazonq.acceptInline')?.args[1]
66103

67104
const sessionId = 'test-session'
68-
const itemId = 'test-item'
69105
const requestStartTime = Date.now() - 1000
70106
const firstCompletionDisplayLatency = 500
71107

72-
await acceptanceHandler(sessionId, itemId, requestStartTime, firstCompletionDisplayLatency)
108+
await acceptanceHandler(
109+
sessionId,
110+
mockSuggestions[0],
111+
mockEditor,
112+
requestStartTime,
113+
firstCompletionDisplayLatency
114+
)
73115

74116
assert(sendNotificationStub.calledOnce)
75117
assert(
@@ -78,7 +120,7 @@ describe('InlineCompletionManager', () => {
78120
sinon.match({
79121
sessionId,
80122
completionSessionResult: {
81-
[itemId]: {
123+
[mockSuggestions[0].itemId]: {
82124
seen: true,
83125
accepted: true,
84126
discarded: false,
@@ -91,6 +133,39 @@ describe('InlineCompletionManager', () => {
91133
assert(disposableStub.calledOnce)
92134
assert(registerProviderStub.calledTwice) // Once in constructor, once after acceptance
93135
})
136+
137+
it('should log reference if there is any', async () => {
138+
const acceptanceHandler = registerCommandStub
139+
.getCalls()
140+
?.find((call) => call.args[0] === 'aws.amazonq.acceptInline')?.args[1]
141+
142+
const sessionId = 'test-session'
143+
const requestStartTime = Date.now() - 1000
144+
const firstCompletionDisplayLatency = 500
145+
const mockReferenceLog = 'test reference log'
146+
getReferenceStub.returns(mockReferenceLog)
147+
148+
await acceptanceHandler(
149+
sessionId,
150+
mockSuggestions[0],
151+
mockEditor,
152+
requestStartTime,
153+
firstCompletionDisplayLatency
154+
)
155+
156+
assert(getReferenceStub.calledOnce)
157+
assert(
158+
getReferenceStub.calledWith(
159+
mockSuggestions[0].insertText,
160+
mockSuggestions[0].references,
161+
mockEditor
162+
)
163+
)
164+
assert(logReferenceStub.calledOnce)
165+
assert(logReferenceStub.calledWith(mockReferenceLog))
166+
assert(hoverReferenceStub.calledOnce)
167+
assert(hoverReferenceStub.calledWith(mockSuggestions[0].insertText, mockSuggestions[0].references))
168+
})
94169
})
95170

96171
describe('onInlineRejection', () => {
@@ -179,4 +254,82 @@ describe('InlineCompletionManager', () => {
179254
})
180255
})
181256
})
257+
258+
describe('AmazonQInlineCompletionItemProvider', () => {
259+
describe('provideInlineCompletionItems', () => {
260+
let mockSessionManager: SessionManager
261+
let provider: AmazonQInlineCompletionItemProvider
262+
let getAllRecommendationsStub: sinon.SinonStub
263+
let recommendationService: RecommendationService
264+
let setInlineReferenceStub: sinon.SinonStub
265+
266+
beforeEach(() => {
267+
recommendationService = new RecommendationService(mockSessionManager)
268+
setInlineReferenceStub = sandbox.stub(ReferenceInlineProvider.instance, 'setInlineReference')
269+
270+
mockSessionManager = {
271+
getActiveSession: getActiveSessionStub,
272+
getActiveRecommendation: getActiveRecommendationStub,
273+
} as unknown as SessionManager
274+
275+
getActiveSessionStub.returns({
276+
sessionId: 'test-session',
277+
suggestions: mockSuggestions,
278+
isRequestInProgress: false,
279+
requestStartTime: Date.now(),
280+
})
281+
getActiveRecommendationStub.returns(mockSuggestions)
282+
getAllRecommendationsStub = sandbox.stub(recommendationService, 'getAllRecommendations')
283+
getAllRecommendationsStub.resolves()
284+
}),
285+
it('should call recommendation service to get new suggestions for new sessions', async () => {
286+
provider = new AmazonQInlineCompletionItemProvider(
287+
languageClient,
288+
recommendationService,
289+
mockSessionManager
290+
)
291+
const items = await provider.provideInlineCompletionItems(
292+
mockDocument,
293+
mockPosition,
294+
mockContext,
295+
mockToken
296+
)
297+
assert(getAllRecommendationsStub.calledOnce)
298+
assert.deepStrictEqual(items, mockSuggestions)
299+
}),
300+
it('should not call recommendation service for existing sessions', async () => {
301+
provider = new AmazonQInlineCompletionItemProvider(
302+
languageClient,
303+
recommendationService,
304+
mockSessionManager,
305+
false
306+
)
307+
const items = await provider.provideInlineCompletionItems(
308+
mockDocument,
309+
mockPosition,
310+
mockContext,
311+
mockToken
312+
)
313+
assert(getAllRecommendationsStub.notCalled)
314+
assert.deepStrictEqual(items, mockSuggestions)
315+
}),
316+
it('should handle reference if there is any', async () => {
317+
provider = new AmazonQInlineCompletionItemProvider(
318+
languageClient,
319+
recommendationService,
320+
mockSessionManager,
321+
false
322+
)
323+
await provider.provideInlineCompletionItems(mockDocument, mockPosition, mockContext, mockToken)
324+
assert(setInlineReferenceStub.calledOnce)
325+
assert(
326+
setInlineReferenceStub.calledWithExactly(
327+
mockPosition.line,
328+
mockSuggestions[0].insertText,
329+
fakeReferences
330+
)
331+
)
332+
})
333+
})
334+
})
182335
})

0 commit comments

Comments
 (0)