@@ -10,9 +10,13 @@ import com.google.gson.Gson
10
10
import com.intellij.ide.BrowserUtil
11
11
import com.intellij.ide.util.RunOnceUtil
12
12
import com.intellij.openapi.application.runInEdt
13
+ import com.intellij.openapi.application.runReadAction
14
+ import com.intellij.openapi.fileEditor.FileDocumentManager
13
15
import com.intellij.openapi.fileEditor.FileEditorManager
14
16
import com.intellij.openapi.options.ShowSettingsUtil
15
17
import com.intellij.openapi.project.Project
18
+ import com.intellij.openapi.vfs.LocalFileSystem
19
+ import com.intellij.openapi.vfs.VirtualFile
16
20
import com.intellij.ui.jcef.JBCefJSQuery.Response
17
21
import kotlinx.coroutines.CancellationException
18
22
import kotlinx.coroutines.CompletableDeferred
@@ -21,6 +25,7 @@ import kotlinx.coroutines.coroutineScope
21
25
import kotlinx.coroutines.flow.Flow
22
26
import kotlinx.coroutines.flow.callbackFlow
23
27
import kotlinx.coroutines.flow.distinctUntilChanged
28
+ import kotlinx.coroutines.flow.first
24
29
import kotlinx.coroutines.flow.launchIn
25
30
import kotlinx.coroutines.flow.merge
26
31
import kotlinx.coroutines.flow.onEach
@@ -42,7 +47,6 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.JsonRpcMethod
42
47
import software.aws.toolkits.jetbrains.services.amazonq.lsp.JsonRpcNotification
43
48
import software.aws.toolkits.jetbrains.services.amazonq.lsp.JsonRpcRequest
44
49
import software.aws.toolkits.jetbrains.services.amazonq.lsp.encryption.JwtEncryptionManager
45
- import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AwsServerCapabilitiesProvider
46
50
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ChatCommunicationManager
47
51
import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.FlareUiMessage
48
52
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.AUTH_FOLLOW_UP_CLICKED
@@ -71,6 +75,8 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_
71
75
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_BAR_ACTIONS
72
76
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_CHANGE
73
77
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_TAB_REMOVE
78
+ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CODE_REVIEW_FINDINGS_SUFFIX
79
+ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.DISPLAY_FINDINGS_SUFFIX
74
80
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedChatParams
75
81
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.EncryptedQuickActionChatParams
76
82
import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GET_SERIALIZED_CHAT_REQUEST_METHOD
@@ -107,10 +113,17 @@ import software.aws.toolkits.jetbrains.services.amazonqCodeTest.auth.isCodeTestA
107
113
import software.aws.toolkits.jetbrains.services.amazonqDoc.auth.isDocAvailable
108
114
import software.aws.toolkits.jetbrains.services.amazonqFeatureDev.auth.isFeatureDevAvailable
109
115
import software.aws.toolkits.jetbrains.services.codemodernizer.utils.isCodeTransformAvailable
116
+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanIssue
117
+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.CodeWhispererCodeScanManager
118
+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Description
119
+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.Recommendation
120
+ import software.aws.toolkits.jetbrains.services.codewhisperer.codescan.SuggestedFix
110
121
import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable
122
+ import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants
111
123
import software.aws.toolkits.jetbrains.settings.MeetQSettings
112
124
import software.aws.toolkits.telemetry.MetricResult
113
125
import software.aws.toolkits.telemetry.Telemetry
126
+ import java.nio.file.Path
114
127
import java.util.concurrent.CompletableFuture
115
128
import java.util.concurrent.CompletionException
116
129
import java.util.function.Function
@@ -568,6 +581,32 @@ class BrowserConnector(
568
581
}
569
582
}
570
583
584
+ data class FlareCodeScanIssue (
585
+ val startLine : Int ,
586
+ val endLine : Int ,
587
+ val comment : String? ,
588
+ val title : String ,
589
+ val description : Description ,
590
+ val detectorId : String ,
591
+ val detectorName : String ,
592
+ val findingId : String ,
593
+ val ruleId : String? ,
594
+ val relatedVulnerabilities : List <String >,
595
+ val severity : String ,
596
+ val recommendation : Recommendation ,
597
+ val suggestedFixes : List <SuggestedFix >,
598
+ val scanJobId : String ,
599
+ val language : String ,
600
+ val autoDetected : Boolean ,
601
+ val filePath : String ,
602
+ val findingContext : String ,
603
+ )
604
+
605
+ data class AggregatedCodeScanIssue (
606
+ val filePath : String ,
607
+ val issues : List <FlareCodeScanIssue >,
608
+ )
609
+
571
610
private fun showResult (
572
611
result : CompletableFuture <String >,
573
612
partialResultToken : String ,
@@ -581,10 +620,14 @@ class BrowserConnector(
581
620
throw error
582
621
}
583
622
chatCommunicationManager.removePartialChatMessage(partialResultToken)
623
+ val decryptedMessage = Gson ().fromJson(value?.let { encryptionManager?.decrypt(it) }.orEmpty(), Map ::class .java)
624
+ as Map <String , * >
625
+ parseFindingsMessages(decryptedMessage)
626
+
584
627
val messageToChat = ChatCommunicationManager .convertToJsonToSendToChat(
585
628
SEND_CHAT_COMMAND_PROMPT ,
586
629
tabId,
587
- value?. let { encryptionManager?.decrypt(it) }.orEmpty( ),
630
+ Gson ().toJson(decryptedMessage ),
588
631
isPartialResult = false
589
632
)
590
633
browser.postChat(messageToChat)
@@ -598,15 +641,98 @@ class BrowserConnector(
598
641
}
599
642
}
600
643
601
- private fun updateQuickActionsInBrowser (browser : Browser ) {
644
+ fun parseFindingsMessages (messagesMap : Map <String , * >) {
645
+ try {
646
+ val additionalMessages = messagesMap[" additionalMessages" ] as ? MutableList <Map <String , Any >>
647
+ val findingsMessages = additionalMessages?.filter { message ->
648
+ if (message.contains(" messageId" )) {
649
+ (message[" messageId" ] as String ).endsWith(CODE_REVIEW_FINDINGS_SUFFIX ) ||
650
+ (message[" messageId" ] as String ).endsWith(DISPLAY_FINDINGS_SUFFIX )
651
+ } else {
652
+ false
653
+ }
654
+ }
655
+ val scannedFiles = mutableListOf<VirtualFile >()
656
+ if (findingsMessages != null ) {
657
+ for (findingsMessage in findingsMessages) {
658
+ additionalMessages.remove(findingsMessage)
659
+ val gson = Gson ()
660
+ val jsonFindings = gson.fromJson(findingsMessage[" body" ] as String , List ::class .java)
661
+ val mappedFindings = mutableListOf<CodeWhispererCodeScanIssue >()
662
+ for (aggregatedIssueUnformatted in jsonFindings) {
663
+ val aggregatedIssue = gson.fromJson(gson.toJson(aggregatedIssueUnformatted), AggregatedCodeScanIssue ::class .java)
664
+ val file = LocalFileSystem .getInstance().findFileByIoFile(
665
+ Path .of(aggregatedIssue.filePath).toFile()
666
+ )
667
+ if (file?.isDirectory == false ) {
668
+ scannedFiles.add(file)
669
+ runReadAction {
670
+ FileDocumentManager .getInstance().getDocument(file)
671
+ }?.let { document ->
672
+ for (issue in aggregatedIssue.issues) {
673
+ val endLineInDocument = minOf(maxOf(0 , issue.endLine - 1 ), document.lineCount - 1 )
674
+ val endCol = document.getLineEndOffset(endLineInDocument) - document.getLineStartOffset(endLineInDocument) + 1
675
+ val isIssueIgnored = CodeWhispererCodeScanManager .getInstance(project)
676
+ .isIgnoredIssue(issue.title, document, file, issue.startLine - 1 )
677
+ if (isIssueIgnored) {
678
+ continue
679
+ }
680
+ mappedFindings.add(
681
+ CodeWhispererCodeScanIssue (
682
+ startLine = issue.startLine,
683
+ startCol = 1 ,
684
+ endLine = issue.endLine,
685
+ endCol = endCol,
686
+ file = file,
687
+ project = project,
688
+ title = issue.title,
689
+ description = issue.description,
690
+ detectorId = issue.detectorId,
691
+ detectorName = issue.detectorName,
692
+ findingId = issue.findingId,
693
+ ruleId = issue.ruleId,
694
+ relatedVulnerabilities = issue.relatedVulnerabilities,
695
+ severity = issue.severity,
696
+ recommendation = issue.recommendation,
697
+ suggestedFixes = issue.suggestedFixes,
698
+ codeSnippet = emptyList(),
699
+ isVisible = true ,
700
+ autoDetected = issue.autoDetected,
701
+ scanJobId = issue.scanJobId,
702
+ ),
703
+ )
704
+ }
705
+ }
706
+ }
707
+ }
708
+
709
+ CodeWhispererCodeScanManager .getInstance(project)
710
+ .addOnDemandIssues(
711
+ mappedFindings,
712
+ scannedFiles,
713
+ CodeWhispererConstants .CodeAnalysisScope .AGENTIC
714
+ )
715
+ CodeWhispererCodeScanManager .getInstance(project).showCodeScanUI()
716
+ }
717
+ }
718
+ } catch (e: Exception ) {
719
+ LOG .error { " Failed to parse findings message $e " }
720
+ }
721
+ }
722
+
723
+ private suspend fun updateQuickActionsInBrowser (browser : Browser ) {
602
724
val isFeatureDevAvailable = isFeatureDevAvailable(project)
603
725
val isCodeTransformAvailable = isCodeTransformAvailable(project)
604
726
val isDocAvailable = isDocAvailable(project)
605
727
val isCodeScanAvailable = isCodeScanAvailable(project)
606
728
val isCodeTestAvailable = isCodeTestAvailable(project)
607
729
730
+ val serverCapabilities = AmazonQLspService .getInstance(project).instanceFlow.first().initializeResult.await().awsServerCapabilities
731
+
732
+ // language=JavaScript
608
733
val script = """
609
734
try {
735
+ // hack to create the list of actions across all tab types
610
736
const tempConnector = connectorAdapter.initiateAdapter(
611
737
false,
612
738
true, // the two values are not used here, needed for constructor
@@ -619,7 +745,7 @@ class BrowserConnector(
619
745
);
620
746
621
747
const commands = tempConnector.initialQuickActions?.slice(0, 2) || [];
622
- const options = ${Gson ().toJson(AwsServerCapabilitiesProvider .getInstance(project).getChatOptions() )} ;
748
+ const options = ${Gson ().toJson(serverCapabilities.chatOptions )} ;
623
749
options.quickActions.quickActionsCommandGroups = [
624
750
...commands,
625
751
...options.quickActions.quickActionsCommandGroups
0 commit comments