Skip to content

Commit 1ef7255

Browse files
authored
feat: add code reference handling to featureDev (#1831)
featureDev, a.k.a /dev in amazonq has two moments where references are handled. One, when it is shown in the diff view chat component, where all references are listed for the customer to see. Two, when the customer accepts code generation where these references are stored in the general chat session. This PR is adding a storage for the reference logs and also sending the references to the chat view with the formatting that /dev expects.
1 parent d773361 commit 1ef7255

File tree

12 files changed

+84
-42
lines changed

12 files changed

+84
-42
lines changed

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,16 +257,18 @@ class FeatureDevController(
257257
AmazonqTelemetry.isAcceptedCodeChanges(amazonqConversationId = session.conversationId, enabled = true)
258258

259259
var filePaths: List<NewFileZipInfo> = emptyList()
260-
var deletedFiles: Array<String> = emptyArray()
260+
var deletedFiles: List<String> = emptyList()
261+
var references: List<CodeReference> = emptyList()
261262

262263
when (val state = session.sessionState) {
263264
is PrepareCodeGenerationState -> {
264265
filePaths = state.filePaths
265266
deletedFiles = state.deletedFiles
267+
references = state.references
266268
}
267269
}
268270

269-
session.insertChanges(filePaths = filePaths, deletedFiles = deletedFiles)
271+
session.insertChanges(filePaths = filePaths, deletedFiles = deletedFiles, references = references)
270272

271273
messenger.sendAnswer(
272274
tabId = tabId,
@@ -460,8 +462,8 @@ class FeatureDevController(
460462
val state = session.sessionState
461463

462464
var filePaths: List<NewFileZipInfo> = emptyList()
463-
var deletedFiles: Array<String> = emptyArray()
464-
var references: Array<CodeReference> = emptyArray()
465+
var deletedFiles: List<String> = emptyList()
466+
var references: List<CodeReference> = emptyList()
465467
var uploadId = ""
466468

467469
when (state) {

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/messages/FeatureDevMessage.kt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import com.fasterxml.jackson.annotation.JsonValue
88
import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthFollowUpType
99
import software.aws.toolkits.jetbrains.services.amazonq.messages.AmazonQMessage
1010
import software.aws.toolkits.jetbrains.services.cwc.messages.CodeReference
11-
import software.aws.toolkits.jetbrains.services.cwc.messages.RecommendationContentSpan
1211
import java.time.Instant
1312
import java.util.UUID
1413

@@ -214,5 +213,4 @@ enum class FollowUpTypes(
214213
// Util classes
215214
data class ReducedCodeReference(
216215
val information: String,
217-
val recommendationContentSpan: RecommendationContentSpan
218216
)

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/messages/FeatureDevMessagePublisherExtensions.kt

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthNeededState
77
import software.aws.toolkits.jetbrains.services.amazonq.messages.MessagePublisher
88
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.NewFileZipInfo
99
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.SessionStatePhase
10+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.licenseText
1011
import software.aws.toolkits.jetbrains.services.cwc.messages.CodeReference
11-
import software.aws.toolkits.jetbrains.services.cwc.messages.RecommendationContentSpan
1212
import software.aws.toolkits.resources.message
1313
import java.util.UUID
1414

@@ -171,23 +171,20 @@ suspend fun MessagePublisher.sendCodeResult(
171171
tabId: String,
172172
uploadId: String,
173173
filePaths: List<NewFileZipInfo>,
174-
deletedFiles: Array<String>,
175-
references: Array<CodeReference>
174+
deletedFiles: List<String>,
175+
references: List<CodeReference>
176176
) {
177177
// It is being done this mapping as featureDev currently doesn't support fully references.
178178
val refs = references.filter { it.licenseName != null && it.url != null && it.repository != null }.map {
179-
ReducedCodeReference(
180-
information = it.information, // TODO: double check this references logic. As we have a licenseText function in vscode toolkit.
181-
recommendationContentSpan = RecommendationContentSpan(start = 0, end = 0)
182-
)
179+
ReducedCodeReference(information = it.licenseText())
183180
}
184181

185182
this.publish(
186183
CodeResultMessage(
187184
tabId = tabId,
188185
conversationId = uploadId,
189186
filePaths = filePaths.map { it.zipFilePath },
190-
deletedFiles = deletedFiles.asList(),
187+
deletedFiles = deletedFiles,
191188
references = refs
192189
)
193190
)

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/session/CodeGenerationState.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ private suspend fun CodeGenerationState.generateCode(codeGenerationId: String):
105105
}
106106
}
107107

108-
return CodeGenerationResult(emptyList(), emptyArray(), emptyArray())
108+
return CodeGenerationResult(emptyList(), emptyList(), emptyList())
109109
}
110110

111111
fun registerNewFiles(newFileContents: Map<String, String>): List<NewFileZipInfo> = newFileContents.map {

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/session/PrepareCodeGenerationState.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ class PrepareCodeGenerationState(
1818
override var approach: String,
1919
private var config: SessionStateConfig,
2020
val filePaths: List<NewFileZipInfo>,
21-
val deletedFiles: Array<String>,
21+
val deletedFiles: List<String>,
22+
val references: List<CodeReference>,
2223
var uploadId: String,
23-
val references: Array<CodeReference>,
2424
private val currentIteration: Int,
2525
private var messenger: MessagePublisher
2626
) : SessionState {

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/session/Session.kt

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.clients.Featur
1111
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.conversationIdNotFound
1212
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAsyncEventProgress
1313
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.createConversation
14+
import software.aws.toolkits.jetbrains.services.cwc.controller.ReferenceLogController
15+
import software.aws.toolkits.jetbrains.services.cwc.messages.CodeReference
1416
import software.aws.toolkits.telemetry.AmazonqTelemetry
1517
import kotlin.io.path.createDirectories
1618
import kotlin.io.path.deleteIfExists
@@ -76,8 +78,8 @@ class Session(val tabID: String, val project: Project) {
7678
approach = sessionState.approach,
7779
config = getSessionStateConfig(),
7880
filePaths = emptyList(),
79-
deletedFiles = emptyArray(),
80-
references = emptyArray(),
81+
deletedFiles = emptyList(),
82+
references = emptyList(),
8183
currentIteration = 0, // first code gen iteration
8284
uploadId = "", // There is no code gen uploadId so far
8385
messenger = messenger,
@@ -90,7 +92,7 @@ class Session(val tabID: String, val project: Project) {
9092
/**
9193
* Triggered by the Insert code follow-up button to apply code changes.
9294
*/
93-
fun insertChanges(filePaths: List<NewFileZipInfo>, deletedFiles: Array<String>) {
95+
fun insertChanges(filePaths: List<NewFileZipInfo>, deletedFiles: List<String>, references: List<CodeReference>) {
9496
val projectRootPath = context.projectRoot.toNioPath()
9597

9698
filePaths.forEach {
@@ -104,7 +106,7 @@ class Session(val tabID: String, val project: Project) {
104106
deleteFilePath.deleteIfExists()
105107
}
106108

107-
// TODO: References received from code generation should be logged.
109+
ReferenceLogController.addReferenceLog(references, project)
108110
}
109111

110112
suspend fun send(msg: String): Interaction {

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/session/SessionStateTypes.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,15 @@ data class NewFileZipInfo(
4343

4444
data class CodeGenerationResult(
4545
var newFiles: List<NewFileZipInfo>,
46-
var deletedFiles: Array<String>, // The string is the path of the file to be deleted
47-
var references: Array<CodeReference>,
46+
var deletedFiles: List<String>, // The string is the path of the file to be deleted
47+
var references: List<CodeReference>,
4848
)
4949

5050
@Suppress("ConstructorParameterNaming") // Unfortunately, this is exactly how the string json is received and is needed for parsing.
5151
data class CodeGenerationStreamResult(
5252
var new_file_contents: Map<String, String>,
53-
var deleted_files: Array<String>,
54-
var references: Array<CodeReference>,
53+
var deleted_files: List<String>,
54+
var references: List<CodeReference>,
5555
)
5656

5757
@Suppress("ConstructorParameterNaming") // Unfortunately, this is exactly how the string json is received and is needed for parsing.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util
5+
6+
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererLicenseInfoManager
7+
import software.aws.toolkits.jetbrains.services.cwc.messages.CodeReference
8+
9+
fun CodeReference.licenseText(): String {
10+
val licenseLink = CodeWhispererLicenseInfoManager.getInstance().getLicenseLink(this.licenseName.orEmpty())
11+
12+
return "<a href=\"${licenseLink}\" target=\"_blank\">" +
13+
"${this.licenseName}" +
14+
"</a> license from repository <a href=\"${this.url}\" target=\"_blank\">${this.repository}</a>"
15+
}

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererLicenseInfoManager.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ class CodeWhispererLicenseInfoManager {
1919
}
2020
}
2121

22-
fun getLicenseLink(code: String) = licenseLinks.getOrDefault(code, "")
22+
fun getLicenseLink(code: String) = licenseLinks.getOrDefault(code, "https://spdx.org/licenses")
2323

2424
companion object {
2525
fun getInstance(): CodeWhispererLicenseInfoManager = service()

plugins/toolkit/jetbrains-core/src/software/aws/toolkits/jetbrains/services/codewhisperer/toolwindow/CodeWhispererCodeReferenceComponents.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ class CodeWhispererCodeReferenceComponents(private val project: Project) {
105105
text = message("codewhisperer.toolwindow.entry.suffix", path ?: "", choice, line)
106106
}.asCodeReferencePanelFont()
107107

108-
fun codeReferenceRecordPanel(ref: Reference, relativePath: String?, lineNums: String) = JPanel(GridBagLayout()).apply {
108+
fun codeReferenceRecordPanel(ref: Reference, relativePath: String?, lineNums: String?) = JPanel(GridBagLayout()).apply {
109109
background = EditorColorsManager.getInstance().globalScheme.defaultBackground
110110
border = BorderFactory.createEmptyBorder(5, 0, 0, 0)
111111
add(acceptRecommendationPrefixText, inlineLabelConstraints)
@@ -132,7 +132,9 @@ class CodeWhispererCodeReferenceComponents(private val project: Project) {
132132
add(repoNameLink(ref.repository(), ref.url()), inlineLabelConstraints)
133133
}
134134

135-
add(acceptRecommendationSuffixText(relativePath, lineNums), inlineLabelConstraints)
135+
if (!lineNums.isNullOrEmpty()) {
136+
add(acceptRecommendationSuffixText(relativePath, lineNums), inlineLabelConstraints)
137+
}
136138
addHorizontalGlue()
137139
}
138140

0 commit comments

Comments
 (0)