Skip to content

Commit ca35efd

Browse files
Merge main into feature/cw-proactive-scan
2 parents 849c7df + c046fb9 commit ca35efd

File tree

12 files changed

+1014
-183
lines changed

12 files changed

+1014
-183
lines changed

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/controller/FeatureDevController.kt

Lines changed: 15 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import com.intellij.diff.contents.EmptyContent
99
import com.intellij.diff.requests.SimpleDiffRequest
1010
import com.intellij.diff.util.DiffUserDataKeys
1111
import com.intellij.ide.BrowserUtil
12-
import com.intellij.notification.NotificationAction
1312
import com.intellij.openapi.application.runInEdt
1413
import com.intellij.openapi.command.WriteCommandAction
1514
import com.intellij.openapi.editor.Caret
@@ -49,19 +48,19 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendA
4948
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAuthNeededException
5049
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAuthenticationInProgressMessage
5150
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendChatInputEnabledMessage
52-
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendCodeResult
5351
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendError
5452
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendSystemPrompt
5553
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendUpdatePlaceholder
54+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.updateFileComponent
5655
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.DeletedFileInfo
5756
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.NewFileZipInfo
5857
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.PrepareCodeGenerationState
5958
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.Session
6059
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.SessionStatePhase
6160
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.storage.ChatSessionStorage
61+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.getFollowUpOptions
6262
import software.aws.toolkits.jetbrains.services.cwc.messages.CodeReference
6363
import software.aws.toolkits.jetbrains.ui.feedback.FeatureDevFeedbackDialog
64-
import software.aws.toolkits.jetbrains.utils.notifyInfo
6564
import software.aws.toolkits.resources.message
6665
import software.aws.toolkits.telemetry.AmazonqTelemetry
6766
import java.util.UUID
@@ -72,8 +71,8 @@ class FeatureDevController(
7271
private val authController: AuthController = AuthController()
7372
) : InboundAppMessagesHandler {
7473

75-
private val messenger = context.messagesFromAppToUi
76-
private val toolWindow = ToolWindowManager.getInstance(context.project).getToolWindow(AmazonQToolWindowFactory.WINDOW_ID)
74+
val messenger = context.messagesFromAppToUi
75+
val toolWindow = ToolWindowManager.getInstance(context.project).getToolWindow(AmazonQToolWindowFactory.WINDOW_ID)
7776

7877
override suspend fun processPromptChatMessage(message: IncomingFeatureDevMessage.ChatPrompt) {
7978
handleChat(
@@ -225,12 +224,7 @@ class FeatureDevController(
225224
filePaths.find { it.zipFilePath == fileToUpdate }?.let { it.rejected = !it.rejected }
226225
deletedFiles.find { it.zipFilePath == fileToUpdate }?.let { it.rejected = !it.rejected }
227226

228-
session.updateFilesPaths(
229-
messenger = messenger,
230-
tabId = message.tabId,
231-
filePaths = filePaths,
232-
deletedFiles = deletedFiles
233-
)
227+
messenger.updateFileComponent(message.tabId, filePaths, deletedFiles)
234228
}
235229

236230
private suspend fun newTabOpened(tabId: String) {
@@ -292,13 +286,18 @@ class FeatureDevController(
292286
references = state.references
293287
}
294288
}
289+
295290
AmazonqTelemetry.isAcceptedCodeChanges(
296-
project = null,
297291
amazonqNumberOfFilesAccepted = (filePaths.filterNot { it.rejected }.size + deletedFiles.filterNot { it.rejected }.size) * 1.0,
298292
amazonqConversationId = session.conversationId,
299293
enabled = true
300294
)
301-
session.insertChanges(filePaths = filePaths, deletedFiles = deletedFiles, references = references)
295+
296+
session.insertChanges(
297+
filePaths = filePaths.filterNot { it.rejected },
298+
deletedFiles = deletedFiles.filterNot { it.rejected },
299+
references = references
300+
)
302301

303302
messenger.sendAnswer(
304303
tabId = tabId,
@@ -502,124 +501,6 @@ class FeatureDevController(
502501
messenger.sendAsyncEventProgress(tabId = tabId, inProgress = false)
503502
}
504503

505-
private suspend fun onCodeGeneration(session: Session, message: String, tabId: String) {
506-
messenger.sendAsyncEventProgress(
507-
tabId = tabId,
508-
inProgress = true,
509-
message = message("amazonqFeatureDev.chat_message.start_code_generation"),
510-
)
511-
512-
try {
513-
messenger.sendAnswer(
514-
tabId = tabId,
515-
message = message("amazonqFeatureDev.chat_message.requesting_changes"),
516-
messageType = FeatureDevMessageType.AnswerStream,
517-
)
518-
519-
messenger.sendUpdatePlaceholder(tabId = tabId, newPlaceholder = message("amazonqFeatureDev.placeholder.generating_code"))
520-
521-
session.send(message) // Trigger code generation
522-
523-
val state = session.sessionState
524-
525-
var filePaths: List<NewFileZipInfo> = emptyList()
526-
var deletedFiles: List<DeletedFileInfo> = emptyList()
527-
var references: List<CodeReference> = emptyList()
528-
var uploadId = ""
529-
530-
when (state) {
531-
is PrepareCodeGenerationState -> {
532-
filePaths = state.filePaths
533-
deletedFiles = state.deletedFiles
534-
references = state.references
535-
uploadId = state.uploadId
536-
}
537-
}
538-
539-
// Atm this is the only possible path as codegen is mocked to return empty.
540-
if (filePaths.size or deletedFiles.size == 0) {
541-
messenger.sendAnswer(
542-
tabId = tabId,
543-
messageType = FeatureDevMessageType.Answer,
544-
message = message("amazonqFeatureDev.code_generation.no_file_changes")
545-
)
546-
messenger.sendSystemPrompt(
547-
tabId = tabId,
548-
followUp = if (retriesRemaining(session) > 0) {
549-
listOf(
550-
FollowUp(
551-
pillText = message("amazonqFeatureDev.follow_up.retry"),
552-
type = FollowUpTypes.RETRY,
553-
status = FollowUpStatusType.Warning
554-
)
555-
)
556-
} else {
557-
emptyList()
558-
}
559-
)
560-
messenger.sendChatInputEnabledMessage(tabId = tabId, enabled = false) // Lock chat input until retry is clicked.
561-
return
562-
}
563-
564-
messenger.sendCodeResult(tabId = tabId, uploadId = uploadId, filePaths = filePaths, deletedFiles = deletedFiles, references = references)
565-
566-
messenger.sendSystemPrompt(tabId = tabId, followUp = getFollowUpOptions(session.sessionState.phase, interactionSucceeded = true))
567-
568-
messenger.sendUpdatePlaceholder(tabId = tabId, newPlaceholder = message("amazonqFeatureDev.placeholder.after_code_generation"))
569-
} finally {
570-
messenger.sendAsyncEventProgress(tabId = tabId, inProgress = false) // Finish processing the event
571-
messenger.sendChatInputEnabledMessage(tabId = tabId, enabled = false) // Lock chat input until a follow-up is clicked.
572-
573-
if (toolWindow != null && !toolWindow.isVisible) {
574-
notifyInfo(
575-
title = message("amazonqFeatureDev.code_generation.notification_title"),
576-
content = message("amazonqFeatureDev.code_generation.notification_message"),
577-
project = context.project,
578-
notificationActions = listOf(openChatNotificationAction())
579-
)
580-
}
581-
}
582-
}
583-
584-
private fun openChatNotificationAction() = NotificationAction.createSimple(message("amazonqFeatureDev.code_generation.notification_open_link")) {
585-
toolWindow?.show()
586-
}
587-
588-
private fun getFollowUpOptions(phase: SessionStatePhase?, interactionSucceeded: Boolean): List<FollowUp> {
589-
when (phase) {
590-
SessionStatePhase.APPROACH -> {
591-
return when (interactionSucceeded) {
592-
true -> listOf(
593-
FollowUp(
594-
pillText = message("amazonqFeatureDev.follow_up.generate_code"),
595-
type = FollowUpTypes.GENERATE_CODE,
596-
status = FollowUpStatusType.Info,
597-
)
598-
)
599-
600-
false -> emptyList()
601-
}
602-
}
603-
SessionStatePhase.CODEGEN -> {
604-
return listOf(
605-
FollowUp(
606-
pillText = message("amazonqFeatureDev.follow_up.insert_code"),
607-
type = FollowUpTypes.INSERT_CODE,
608-
icon = FollowUpIcons.Ok,
609-
status = FollowUpStatusType.Success
610-
),
611-
FollowUp(
612-
pillText = message("amazonqFeatureDev.follow_up.provide_feedback_and_regenerate"),
613-
type = FollowUpTypes.PROVIDE_FEEDBACK_AND_REGENERATE_CODE,
614-
icon = FollowUpIcons.Refresh,
615-
status = FollowUpStatusType.Info
616-
)
617-
)
618-
}
619-
else -> return emptyList()
620-
}
621-
}
622-
623504
private suspend fun retryRequests(tabId: String) {
624505
var session: Session? = null
625506
try {
@@ -728,9 +609,11 @@ class FeatureDevController(
728609
}
729610
}
730611

612+
fun getProject() = context.project
613+
731614
private fun getSessionInfo(tabId: String) = chatSessionStorage.getSession(tabId, context.project)
732615

733-
private fun retriesRemaining(session: Session?): Int = session?.retries ?: DEFAULT_RETRY_LIMIT
616+
fun retriesRemaining(session: Session?): Int = session?.retries ?: DEFAULT_RETRY_LIMIT
734617

735618
companion object {
736619
private val logger = getLogger<FeatureDevController>()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
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.controller
5+
6+
import com.intellij.notification.NotificationAction
7+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.FeatureDevMessageType
8+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.FollowUp
9+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.FollowUpStatusType
10+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.FollowUpTypes
11+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAnswer
12+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAsyncEventProgress
13+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendChatInputEnabledMessage
14+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendCodeResult
15+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendSystemPrompt
16+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendUpdatePlaceholder
17+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.DeletedFileInfo
18+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.NewFileZipInfo
19+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.PrepareCodeGenerationState
20+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.Session
21+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.getFollowUpOptions
22+
import software.aws.toolkits.jetbrains.services.cwc.messages.CodeReference
23+
import software.aws.toolkits.jetbrains.utils.notifyInfo
24+
import software.aws.toolkits.resources.message
25+
26+
suspend fun FeatureDevController.onCodeGeneration(session: Session, message: String, tabId: String) {
27+
messenger.sendAsyncEventProgress(
28+
tabId = tabId,
29+
inProgress = true,
30+
message = message("amazonqFeatureDev.chat_message.start_code_generation"),
31+
)
32+
33+
try {
34+
this.messenger.sendAnswer(
35+
tabId = tabId,
36+
message = message("amazonqFeatureDev.chat_message.requesting_changes"),
37+
messageType = FeatureDevMessageType.AnswerStream,
38+
)
39+
40+
messenger.sendUpdatePlaceholder(tabId = tabId, newPlaceholder = message("amazonqFeatureDev.placeholder.generating_code"))
41+
42+
session.send(message) // Trigger code generation
43+
44+
val state = session.sessionState
45+
46+
var filePaths: List<NewFileZipInfo> = emptyList()
47+
var deletedFiles: List<DeletedFileInfo> = emptyList()
48+
var references: List<CodeReference> = emptyList()
49+
var uploadId = ""
50+
51+
when (state) {
52+
is PrepareCodeGenerationState -> {
53+
filePaths = state.filePaths
54+
deletedFiles = state.deletedFiles
55+
references = state.references
56+
uploadId = state.uploadId
57+
}
58+
}
59+
60+
// Atm this is the only possible path as codegen is mocked to return empty.
61+
if (filePaths.size or deletedFiles.size == 0) {
62+
messenger.sendAnswer(
63+
tabId = tabId,
64+
messageType = FeatureDevMessageType.Answer,
65+
message = message("amazonqFeatureDev.code_generation.no_file_changes")
66+
)
67+
messenger.sendSystemPrompt(
68+
tabId = tabId,
69+
followUp = if (retriesRemaining(session) > 0) {
70+
listOf(
71+
FollowUp(
72+
pillText = message("amazonqFeatureDev.follow_up.retry"),
73+
type = FollowUpTypes.RETRY,
74+
status = FollowUpStatusType.Warning
75+
)
76+
)
77+
} else {
78+
emptyList()
79+
}
80+
)
81+
messenger.sendChatInputEnabledMessage(tabId = tabId, enabled = false) // Lock chat input until retry is clicked.
82+
return
83+
}
84+
85+
messenger.sendCodeResult(tabId = tabId, uploadId = uploadId, filePaths = filePaths, deletedFiles = deletedFiles, references = references)
86+
87+
messenger.sendSystemPrompt(tabId = tabId, followUp = getFollowUpOptions(session.sessionState.phase, interactionSucceeded = true))
88+
89+
messenger.sendUpdatePlaceholder(tabId = tabId, newPlaceholder = message("amazonqFeatureDev.placeholder.after_code_generation"))
90+
} finally {
91+
messenger.sendAsyncEventProgress(tabId = tabId, inProgress = false) // Finish processing the event
92+
messenger.sendChatInputEnabledMessage(tabId = tabId, enabled = false) // Lock chat input until a follow-up is clicked.
93+
94+
if (toolWindow != null && !toolWindow.isVisible) {
95+
notifyInfo(
96+
title = message("amazonqFeatureDev.code_generation.notification_title"),
97+
content = message("amazonqFeatureDev.code_generation.notification_message"),
98+
project = getProject(),
99+
notificationActions = listOf(openChatNotificationAction())
100+
)
101+
}
102+
}
103+
}
104+
105+
private fun FeatureDevController.openChatNotificationAction() = NotificationAction.createSimple(
106+
message("amazonqFeatureDev.code_generation.notification_open_link")
107+
) {
108+
toolWindow?.show()
109+
}

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/session/CodeGenerationState.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,8 @@ class CodeGenerationState(
6464
messenger = messenger,
6565
)
6666

67-
// It is not needed to interact right away with the code generation state.
68-
// returns a SessionStateInteraction object to be handled by the controller.
67+
// It is not needed to interact right away with the PrepareCodeGeneration.
68+
// returns therefore a SessionStateInteraction object to be handled by the controller.
6969
return SessionStateInteraction(
7070
nextState = nextState,
7171
interaction = Interaction(content = "", interactionSucceeded = true)

plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqFeatureDev/session/Session.kt

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,12 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.CODE_GENERATIO
1212
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.clients.FeatureDevClient
1313
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.conversationIdNotFound
1414
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.sendAsyncEventProgress
15-
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.messages.updateFileComponent
1615
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.createConversation
16+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.resolveAndCreateOrUpdateFile
17+
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.resolveAndDeleteFile
1718
import software.aws.toolkits.jetbrains.services.cwc.controller.ReferenceLogController
1819
import software.aws.toolkits.jetbrains.services.cwc.messages.CodeReference
1920
import software.aws.toolkits.telemetry.AmazonqTelemetry
20-
import kotlin.io.path.createDirectories
21-
import kotlin.io.path.deleteIfExists
22-
import kotlin.io.path.writeBytes
2321

2422
class Session(val tabID: String, val project: Project) {
2523
var context: FeatureDevSessionContext
@@ -92,35 +90,15 @@ class Session(val tabID: String, val project: Project) {
9290
AmazonqTelemetry.isApproachAccepted(amazonqConversationId = conversationId, enabled = true)
9391
}
9492

95-
suspend fun updateFilesPaths(
96-
messenger: MessagePublisher,
97-
tabId: String,
98-
filePaths: List<NewFileZipInfo>,
99-
deletedFiles: List<DeletedFileInfo>
100-
) {
101-
messenger.updateFileComponent(tabId, filePaths, deletedFiles)
102-
}
103-
10493
/**
10594
* Triggered by the Insert code follow-up button to apply code changes.
10695
*/
10796
fun insertChanges(filePaths: List<NewFileZipInfo>, deletedFiles: List<DeletedFileInfo>, references: List<CodeReference>) {
10897
val projectRootPath = context.projectRoot.toNioPath()
10998

110-
filePaths
111-
.filterNot { it.rejected }
112-
.forEach {
113-
val filePath = projectRootPath.resolve(it.zipFilePath)
114-
filePath.parent.createDirectories() // Create directories if needed
115-
filePath.writeBytes(it.fileContent.toByteArray(Charsets.UTF_8))
116-
}
99+
filePaths.forEach { resolveAndCreateOrUpdateFile(projectRootPath, it.zipFilePath, it.fileContent) }
117100

118-
deletedFiles
119-
.filterNot { it.rejected }
120-
.forEach {
121-
val deleteFilePath = projectRootPath.resolve(it.zipFilePath)
122-
deleteFilePath.deleteIfExists()
123-
}
101+
deletedFiles.forEach { resolveAndDeleteFile(projectRootPath, it.zipFilePath) }
124102

125103
ReferenceLogController.addReferenceLog(references, project)
126104

@@ -171,6 +149,7 @@ class Session(val tabID: String, val project: Project) {
171149
return _conversationId as String
172150
}
173151
}
152+
174153
val sessionState: SessionState
175154
get() {
176155
if (_state == null) {

0 commit comments

Comments
 (0)