Skip to content

Commit ffb6ec1

Browse files
authored
fix(amazonq): standardize reference type for reference log (#7311)
## Problem References are not added to the reference log at the bottom. The root cause is that the reference tracker log is expecting CW shaped references, but we are passing "Flare" shaped ones. ## Solution - Update the reference log to standardize the shape of incoming references. ## Testing and Verification - added unit test. - Verified e2e: https://github.com/user-attachments/assets/620f6f3d-1205-405e-96a2-322d5e8220c4 - --- - 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 692847a commit ffb6ec1

File tree

2 files changed

+97
-21
lines changed

2 files changed

+97
-21
lines changed

packages/amazonq/test/unit/codewhisperer/service/referenceLogViewProvider.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import assert from 'assert'
66
import { createMockTextEditor, resetCodeWhispererGlobalVariables } from 'aws-core-vscode/test'
77
import { ReferenceLogViewProvider, LicenseUtil } from 'aws-core-vscode/codewhisperer'
8-
98
describe('referenceLogViewProvider', function () {
109
beforeEach(async function () {
1110
await resetCodeWhispererGlobalVariables()
@@ -66,4 +65,39 @@ describe('referenceLogViewProvider', function () {
6665
assert.ok(!actual.includes(LicenseUtil.getLicenseHtml('MIT')))
6766
})
6867
})
68+
69+
it('accepts references from CW and language server', async function () {
70+
const cwReference = {
71+
licenseName: 'MIT',
72+
repository: 'TEST_REPO',
73+
url: 'cw.com',
74+
recommendationContentSpan: {
75+
start: 0,
76+
end: 10,
77+
},
78+
}
79+
80+
const flareReference = {
81+
referenceName: 'test reference',
82+
referenceUrl: 'flare.com',
83+
licenseName: 'apache',
84+
position: {
85+
startCharacter: 0,
86+
endCharacter: 10,
87+
},
88+
}
89+
90+
const actual = ReferenceLogViewProvider.getReferenceLog(
91+
'',
92+
[cwReference, flareReference],
93+
createMockTextEditor()
94+
)
95+
96+
assert.ok(actual.includes('MIT'))
97+
assert.ok(actual.includes('apache'))
98+
assert.ok(actual.includes('TEST_REPO'))
99+
assert.ok(actual.includes('test reference'))
100+
assert.ok(actual.includes('flare.com'))
101+
assert.ok(actual.includes('cw.com'))
102+
})
69103
})

packages/core/src/codewhisperer/service/referenceLogViewProvider.ts

Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
*/
55

66
import * as vscode from 'vscode'
7-
import { References } from '../client/codewhisperer'
87
import { LicenseUtil } from '../util/licenseUtil'
98
import * as CodeWhispererConstants from '../models/constants'
109
import { CodeWhispererSettings } from '../util/codewhispererSettings'
1110
import globals from '../../shared/extensionGlobals'
1211
import { AuthUtil } from '../util/authUtil'
1312
import { session } from '../util/codeWhispererSession'
13+
import CodeWhispererClient from '../client/codewhispererclient'
14+
import CodeWhispererUserClient from '../client/codewhispereruserclient'
15+
import { InlineCompletionItemWithReferences } from '@aws/language-server-runtimes-types'
1416

1517
export class ReferenceLogViewProvider implements vscode.WebviewViewProvider {
1618
public static readonly viewType = 'aws.codeWhisperer.referenceLog'
@@ -52,28 +54,23 @@ export class ReferenceLogViewProvider implements vscode.WebviewViewProvider {
5254
}
5355
}
5456

55-
public static getReferenceLog(recommendation: string, references: References, editor: vscode.TextEditor): string {
57+
public static getReferenceLog(recommendation: string, references: Reference[], editor: vscode.TextEditor): string {
5658
const filePath = editor.document.uri.path
5759
const time = new Date().toLocaleString()
5860
let text = ``
5961
for (const reference of references) {
62+
const standardReference = toStandardReference(reference)
6063
if (
61-
reference.recommendationContentSpan === undefined ||
62-
reference.recommendationContentSpan.start === undefined ||
63-
reference.recommendationContentSpan.end === undefined
64+
standardReference.position === undefined ||
65+
standardReference.position.start === undefined ||
66+
standardReference.position.end === undefined
6467
) {
6568
continue
6669
}
67-
const code = recommendation.substring(
68-
reference.recommendationContentSpan.start,
69-
reference.recommendationContentSpan.end
70-
)
71-
const firstCharLineNumber =
72-
editor.document.positionAt(session.startCursorOffset + reference.recommendationContentSpan.start).line +
73-
1
74-
const lastCharLineNumber =
75-
editor.document.positionAt(session.startCursorOffset + reference.recommendationContentSpan.end - 1)
76-
.line + 1
70+
const { start, end } = standardReference.position
71+
const code = recommendation.substring(start, end)
72+
const firstCharLineNumber = editor.document.positionAt(session.startCursorOffset + start).line + 1
73+
const lastCharLineNumber = editor.document.positionAt(session.startCursorOffset + end - 1).line + 1
7774
let lineInfo = ``
7875
if (firstCharLineNumber === lastCharLineNumber) {
7976
lineInfo = `(line at ${firstCharLineNumber})`
@@ -84,11 +81,11 @@ export class ReferenceLogViewProvider implements vscode.WebviewViewProvider {
8481
text += `And `
8582
}
8683

87-
let license = `<a href=${LicenseUtil.getLicenseHtml(reference.licenseName)}>${reference.licenseName}</a>`
88-
let repository = reference.repository?.length ? reference.repository : 'unknown'
89-
if (reference.url?.length) {
90-
repository = `<a href=${reference.url}>${reference.repository}</a>`
91-
license = `<b><i>${reference.licenseName || 'unknown'}</i></b>`
84+
let license = `<a href=${LicenseUtil.getLicenseHtml(standardReference.licenseName)}>${standardReference.licenseName}</a>`
85+
let repository = standardReference.repository?.length ? standardReference.repository : 'unknown'
86+
if (standardReference.url?.length) {
87+
repository = `<a href=${standardReference.url}>${standardReference.repository}</a>`
88+
license = `<b><i>${standardReference.licenseName || 'unknown'}</i></b>`
9289
}
9390

9491
text +=
@@ -144,3 +141,48 @@ export class ReferenceLogViewProvider implements vscode.WebviewViewProvider {
144141
</html>`
145142
}
146143
}
144+
145+
/**
146+
* Reference log needs to support references directly from CW, as well as those from Flare. These references have different shapes, so we standarize them here.
147+
*/
148+
type GetInnerType<T> = T extends (infer U)[] ? U : never
149+
type Reference =
150+
| CodeWhispererClient.Reference
151+
| CodeWhispererUserClient.Reference
152+
| GetInnerType<InlineCompletionItemWithReferences['references']>
153+
154+
type StandardizedReference = {
155+
licenseName?: string
156+
position?: {
157+
start?: number
158+
end?: number
159+
}
160+
repository?: string
161+
url?: string
162+
}
163+
164+
/**
165+
* Convert a general reference to the standardized format expected by the reference log.
166+
* @param ref
167+
* @returns
168+
*/
169+
function toStandardReference(ref: Reference): StandardizedReference {
170+
const isCWReference = (ref: any) => ref.recommendationContentSpan !== undefined
171+
172+
if (isCWReference(ref)) {
173+
const castRef = ref as CodeWhispererClient.Reference
174+
return {
175+
licenseName: castRef.licenseName!,
176+
position: { start: castRef.recommendationContentSpan?.start, end: castRef.recommendationContentSpan?.end },
177+
repository: castRef.repository,
178+
url: castRef.url,
179+
}
180+
}
181+
const castRef = ref as GetInnerType<InlineCompletionItemWithReferences['references']>
182+
return {
183+
licenseName: castRef.licenseName,
184+
position: { start: castRef.position?.startCharacter, end: castRef.position?.endCharacter },
185+
repository: castRef.referenceName,
186+
url: castRef.referenceUrl,
187+
}
188+
}

0 commit comments

Comments
 (0)