@@ -14,9 +14,11 @@ import com.intellij.openapi.command.WriteCommandAction
1414import com.intellij.openapi.editor.Caret
1515import com.intellij.openapi.editor.Editor
1616import com.intellij.openapi.fileEditor.FileEditorManager
17+ import com.intellij.openapi.fileEditor.OpenFileDescriptor
1718import com.intellij.openapi.project.Project
1819import com.intellij.openapi.vfs.VfsUtil
1920import com.intellij.openapi.wm.ToolWindowManager
21+ import kotlinx.coroutines.delay
2022import kotlinx.coroutines.withContext
2123import software.amazon.awssdk.services.toolkittelemetry.model.Sentiment
2224import software.aws.toolkits.core.utils.debug
@@ -65,6 +67,8 @@ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.Prepar
6567import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.Session
6668import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.session.SessionStatePhase
6769import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.storage.ChatSessionStorage
70+ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.InsertAction
71+ import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.getFollowUpOptions
6872import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.util.selectFolder
6973import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.FeedbackComment
7074import software.aws.toolkits.jetbrains.services.cwc.controller.chat.telemetry.getStartUrl
@@ -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,74 @@ 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+ val isAllApplied = filePaths.all { it.changeApplied } && deletedFiles.all { it.changeApplied }
269+ val isAllRejected = filePaths.all { it.rejected } && deletedFiles.all { it.rejected }
270+ val isAnyRejected = filePaths.any { it.rejected } || deletedFiles.any { it.rejected }
271+
272+ return if (isAllRejected) {
273+ InsertAction .CONTINUE
274+ } else if (isAnyRejected) {
275+ InsertAction .REMAINING
276+ } else if (isAllApplied) {
277+ InsertAction .AUTO_CONTINUE
278+ } else {
279+ InsertAction .ALL
280+ }
281+ }
282+
283+ val prevInsertAction = insertAction()
284+
285+ if (action == " accept-change" ) {
286+ session.insertChanges(
287+ filePaths = filePaths.filter { it.zipFilePath == fileToUpdate },
288+ deletedFiles = deletedFiles.filter { it.zipFilePath == fileToUpdate },
289+ references = emptyList(),
290+ messenger
291+ )
292+
293+ AmazonqTelemetry .isAcceptedCodeChanges(
294+ amazonqNumberOfFilesAccepted = 1.0 ,
295+ amazonqConversationId = session.conversationId,
296+ enabled = true ,
297+ credentialStartUrl = getStartUrl(project = context.project)
298+ )
299+ } else {
300+ // Mark the file as rejected or not depending on the previous state
301+ filePaths.find { it.zipFilePath == fileToUpdate }?.let { it.rejected = ! it.rejected }
302+ deletedFiles.find { it.zipFilePath == fileToUpdate }?.let { it.rejected = ! it.rejected }
303+ }
260304
305+ // Update the state of the tree view:
261306 messenger.updateFileComponent(message.tabId, filePaths, deletedFiles, messageId)
307+
308+ // Then, if the accepted file is not a deletion, open it:
309+ if (action == " accept-change" && deletedFiles.none { it.zipFilePath == fileToUpdate }) {
310+ var pollAttempt = 0
311+ val pollDelayMs = 10L
312+ while (pollAttempt < 5 ) {
313+ val file = VfsUtil .findRelativeFile(message.filePath, session.context.selectedSourceFolder)
314+ if (file != null ) {
315+ runInEdt {
316+ OpenFileDescriptor (context.project, file).navigate(true )
317+ }
318+ break
319+ } else {
320+ pollAttempt++
321+ delay(pollDelayMs)
322+ }
323+ }
324+ }
325+
326+ val nextInsertAction = insertAction()
327+
328+ if (nextInsertAction == InsertAction .AUTO_CONTINUE ) {
329+ // Insert remaining changes (noop, as there are none), and advance to the next prompt:
330+ insertCode(message.tabId)
331+ } else if (nextInsertAction != prevInsertAction) {
332+ // Update the action displayed to the customer based on the current state:
333+ messenger.sendSystemPrompt(message.tabId, getFollowUpOptions(session.sessionState.phase, nextInsertAction))
334+ }
262335 }
263336
264337 private suspend fun newTabOpened (tabId : String ) {
@@ -325,8 +398,12 @@ class FeatureDevController(
325398 }
326399 }
327400
401+ val rejectedFilesCount = filePaths.count { it.rejected } + deletedFiles.count { it.rejected }
402+ val acceptedFilesCount = filePaths.count { it.changeApplied } + filePaths.count { it.changeApplied }
403+ val remainingFilesCount = filePaths.count() + deletedFiles.count() - acceptedFilesCount - rejectedFilesCount
404+
328405 AmazonqTelemetry .isAcceptedCodeChanges(
329- amazonqNumberOfFilesAccepted = (filePaths.filterNot { it.rejected }.size + deletedFiles.filterNot { it.rejected }.size) * 1.0 ,
406+ amazonqNumberOfFilesAccepted = remainingFilesCount * 1.0 ,
330407 amazonqConversationId = session.conversationId,
331408 enabled = true ,
332409 credentialStartUrl = getStartUrl(project = context.project)
@@ -335,7 +412,8 @@ class FeatureDevController(
335412 session.insertChanges(
336413 filePaths = filePaths.filterNot { it.rejected },
337414 deletedFiles = deletedFiles.filterNot { it.rejected },
338- references = references
415+ references = references,
416+ messenger
339417 )
340418
341419 messenger.sendAnswer(
@@ -377,8 +455,11 @@ class FeatureDevController(
377455 }
378456
379457 private suspend fun newTask (tabId : String , isException : Boolean? = false) {
458+ this .disablePreviousFileList(tabId)
459+
380460 val session = getSessionInfo(tabId)
381461 val sessionLatency = System .currentTimeMillis() - session.sessionStartTime
462+
382463 AmazonqTelemetry .endChat(
383464 amazonqConversationId = session.conversationId,
384465 amazonqEndOfTheConversationLatency = sessionLatency.toDouble(),
@@ -399,6 +480,7 @@ class FeatureDevController(
399480 }
400481
401482 private suspend fun closeSession (tabId : String ) {
483+ this .disablePreviousFileList(tabId)
402484 messenger.sendAnswer(
403485 tabId = tabId,
404486 messageType = FeatureDevMessageType .Answer ,
@@ -503,7 +585,7 @@ class FeatureDevController(
503585 tabId = tabId,
504586 followUp = listOf (
505587 FollowUp (
506- pillText = message(" amazonqFeatureDev.follow_up.insert_code " ),
588+ pillText = message(" amazonqFeatureDev.follow_up.insert_all_code " ),
507589 type = FollowUpTypes .INSERT_CODE ,
508590 icon = FollowUpIcons .Ok ,
509591 status = FollowUpStatusType .Success ,
@@ -546,11 +628,28 @@ class FeatureDevController(
546628 }
547629 }
548630
631+ private suspend fun disablePreviousFileList (tabId : String ) {
632+ val session = getSessionInfo(tabId)
633+ when (val sessionState = session.sessionState) {
634+ is PrepareCodeGenerationState -> {
635+ session.disableFileList(sessionState.filePaths, sessionState.deletedFiles, messenger)
636+ }
637+ }
638+ }
639+
640+ private fun storeCodeResultMessageId (message : IncomingFeatureDevMessage .StoreMessageIdMessage ) {
641+ val tabId = message.tabId
642+ val session = getSessionInfo(tabId)
643+ session.storeCodeResultMessageId(message)
644+ }
645+
549646 private suspend fun handleChat (
550647 tabId : String ,
551648 message : String ,
552649 ) {
553650 var session: Session ? = null
651+
652+ this .disablePreviousFileList(tabId)
554653 try {
555654 logger.debug { " $FEATURE_NAME : Processing message: $message " }
556655 session = getSessionInfo(tabId)
0 commit comments