@@ -17,6 +17,7 @@ import com.intellij.openapi.fileEditor.FileEditorManager
1717import com.intellij.openapi.project.Project
1818import com.intellij.openapi.vfs.VfsUtil
1919import com.intellij.openapi.wm.ToolWindowManager
20+ import kotlinx.coroutines.delay
2021import kotlinx.coroutines.withContext
2122import software.amazon.awssdk.services.toolkittelemetry.model.Sentiment
2223import software.aws.toolkits.core.utils.debug
@@ -65,7 +66,10 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.Prepar
6566import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.Session
6667import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.SessionStatePhase
6768import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.storage.ChatSessionStorage
69+ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.InsertAction
70+ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.getFollowUpOptions
6871import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.selectFolder
72+ import software.aws.toolkits.jetbrains.services.codewhisperer.util.content
6973import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.FeedbackComment
7074import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
7175import software.aws.toolkits.jetbrains.services.telemetry.TelemetryService
@@ -93,6 +97,10 @@ class FeatureDevController(
9397 )
9498 }
9599
100+ override suspend fun processStoreCodeResultMessageId (message : IncomingFeatureDevMessage .StoreMessageIdMessage ) {
101+ storeCodeResultMessageId(message)
102+ }
103+
96104 override suspend fun processStopMessage (message : IncomingFeatureDevMessage .StopResponse ) {
97105 handleStopMessage(message)
98106 }
@@ -126,6 +134,7 @@ class FeatureDevController(
126134
127135 override suspend fun processChatItemVotedMessage (message : IncomingFeatureDevMessage .ChatItemVotedMessage ) {
128136 logger.debug { " $FEATURE_NAME : Processing ChatItemVotedMessage: $message " }
137+ this .disablePreviousFileList(message.tabId)
129138
130139 val session = chatSessionStorage.getSession(message.tabId, context.project)
131140 when (message.vote) {
@@ -244,6 +253,7 @@ class FeatureDevController(
244253 val fileToUpdate = message.filePath
245254 val session = getSessionInfo(message.tabId)
246255 val messageId = message.messageId
256+ val action = message.actionName
247257
248258 var filePaths: List <NewFileZipInfo > = emptyList()
249259 var deletedFiles: List <DeletedFileInfo > = emptyList()
@@ -254,11 +264,69 @@ class FeatureDevController(
254264 }
255265 }
256266
257- // Mark the file as rejected or not depending on the previous state
258- filePaths.find { it.zipFilePath == fileToUpdate }?.let { it.rejected = ! it.rejected }
259- deletedFiles.find { it.zipFilePath == fileToUpdate }?.let { it.rejected = ! it.rejected }
267+ fun insertAction (): InsertAction =
268+ if (filePaths.all { it.changeApplied } && deletedFiles.all { it.changeApplied }) {
269+ InsertAction .AUTO_CONTINUE
270+ } else if (filePaths.all { it.changeApplied || it.rejected } && deletedFiles.all { it.changeApplied || it.rejected }) {
271+ InsertAction .CONTINUE
272+ } else if (filePaths.any { it.changeApplied || it.rejected } || deletedFiles.any { it.changeApplied || it.rejected }) {
273+ InsertAction .REMAINING
274+ } else {
275+ InsertAction .ALL
276+ }
260277
278+ val prevInsertAction = insertAction()
279+
280+ if (action == " accept-change" ) {
281+ session.insertChanges(
282+ filePaths = filePaths.filter { it.zipFilePath == fileToUpdate },
283+ deletedFiles = deletedFiles.filter { it.zipFilePath == fileToUpdate },
284+ references = emptyList(),
285+ messenger
286+ )
287+
288+ AmazonqTelemetry .isAcceptedCodeChanges(
289+ amazonqNumberOfFilesAccepted = 1.0 ,
290+ amazonqConversationId = session.conversationId,
291+ enabled = true ,
292+ credentialStartUrl = getStartUrl(project = context.project)
293+ )
294+ } else {
295+ // Mark the file as rejected or not depending on the previous state
296+ filePaths.find { it.zipFilePath == fileToUpdate }?.let { it.rejected = ! it.rejected }
297+ deletedFiles.find { it.zipFilePath == fileToUpdate }?.let { it.rejected = ! it.rejected }
298+ }
299+
300+ // Update the state of the tree view:
261301 messenger.updateFileComponent(message.tabId, filePaths, deletedFiles, messageId)
302+
303+ // Then, if the accepted file is not a deletion, open a diff to show the changes are applied:
304+ if (action == " accept-change" && deletedFiles.none { it.zipFilePath == fileToUpdate }) {
305+ var pollAttempt = 0
306+ val pollDelayMs = 10L
307+ while (pollAttempt < 5 ) {
308+ val file = VfsUtil .findRelativeFile(message.filePath, session.context.selectedSourceFolder)
309+ // Wait for the file to be created and/or updated to the new content:
310+ if (file != null && file.content() == filePaths.find { it.zipFilePath == fileToUpdate }?.fileContent) {
311+ // Open a diff, showing the changes have been applied and the file now has identical left/right state:
312+ this .processOpenDiff(IncomingFeatureDevMessage .OpenDiff (message.tabId, fileToUpdate, false ))
313+ break
314+ } else {
315+ pollAttempt++
316+ delay(pollDelayMs)
317+ }
318+ }
319+ }
320+
321+ val nextInsertAction = insertAction()
322+
323+ if (nextInsertAction == InsertAction .AUTO_CONTINUE ) {
324+ // Insert remaining changes (noop, as there are none), and advance to the next prompt:
325+ insertCode(message.tabId)
326+ } else if (nextInsertAction != prevInsertAction) {
327+ // Update the action displayed to the customer based on the current state:
328+ messenger.sendSystemPrompt(message.tabId, getFollowUpOptions(session.sessionState.phase, nextInsertAction))
329+ }
262330 }
263331
264332 private suspend fun newTabOpened (tabId : String ) {
@@ -325,8 +393,12 @@ class FeatureDevController(
325393 }
326394 }
327395
396+ val rejectedFilesCount = filePaths.count { it.rejected } + deletedFiles.count { it.rejected }
397+ val acceptedFilesCount = filePaths.count { it.changeApplied } + filePaths.count { it.changeApplied }
398+ val remainingFilesCount = filePaths.count() + deletedFiles.count() - acceptedFilesCount - rejectedFilesCount
399+
328400 AmazonqTelemetry .isAcceptedCodeChanges(
329- amazonqNumberOfFilesAccepted = (filePaths.filterNot { it.rejected }.size + deletedFiles.filterNot { it.rejected }.size) * 1.0 ,
401+ amazonqNumberOfFilesAccepted = remainingFilesCount * 1.0 ,
330402 amazonqConversationId = session.conversationId,
331403 enabled = true ,
332404 credentialStartUrl = getStartUrl(project = context.project)
@@ -335,7 +407,8 @@ class FeatureDevController(
335407 session.insertChanges(
336408 filePaths = filePaths.filterNot { it.rejected },
337409 deletedFiles = deletedFiles.filterNot { it.rejected },
338- references = references
410+ references = references,
411+ messenger
339412 )
340413
341414 messenger.sendAnswer(
@@ -377,8 +450,11 @@ class FeatureDevController(
377450 }
378451
379452 private suspend fun newTask (tabId : String , isException : Boolean? = false) {
453+ this .disablePreviousFileList(tabId)
454+
380455 val session = getSessionInfo(tabId)
381456 val sessionLatency = System .currentTimeMillis() - session.sessionStartTime
457+
382458 AmazonqTelemetry .endChat(
383459 amazonqConversationId = session.conversationId,
384460 amazonqEndOfTheConversationLatency = sessionLatency.toDouble(),
@@ -399,6 +475,7 @@ class FeatureDevController(
399475 }
400476
401477 private suspend fun closeSession (tabId : String ) {
478+ this .disablePreviousFileList(tabId)
402479 messenger.sendAnswer(
403480 tabId = tabId,
404481 messageType = FeatureDevMessageType .Answer ,
@@ -503,7 +580,7 @@ class FeatureDevController(
503580 tabId = tabId,
504581 followUp = listOf (
505582 FollowUp (
506- pillText = message(" amazonqFeatureDev.follow_up.insert_code " ),
583+ pillText = message(" amazonqFeatureDev.follow_up.insert_all_code " ),
507584 type = FollowUpTypes .INSERT_CODE ,
508585 icon = FollowUpIcons .Ok ,
509586 status = FollowUpStatusType .Success ,
@@ -546,11 +623,28 @@ class FeatureDevController(
546623 }
547624 }
548625
626+ private suspend fun disablePreviousFileList (tabId : String ) {
627+ val session = getSessionInfo(tabId)
628+ when (val sessionState = session.sessionState) {
629+ is PrepareCodeGenerationState -> {
630+ session.disableFileList(sessionState.filePaths, sessionState.deletedFiles, messenger)
631+ }
632+ }
633+ }
634+
635+ private fun storeCodeResultMessageId (message : IncomingFeatureDevMessage .StoreMessageIdMessage ) {
636+ val tabId = message.tabId
637+ val session = getSessionInfo(tabId)
638+ session.storeCodeResultMessageId(message)
639+ }
640+
549641 private suspend fun handleChat (
550642 tabId : String ,
551643 message : String ,
552644 ) {
553645 var session: Session ? = null
646+
647+ this .disablePreviousFileList(tabId)
554648 try {
555649 logger.debug { " $FEATURE_NAME : Processing message: $message " }
556650 session = getSessionInfo(tabId)
0 commit comments