From b57c9d6bfb77110120a59598c653a5495e85bac4 Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Mon, 19 May 2025 15:16:36 -0700 Subject: [PATCH 1/9] fix(amazonq): Files.move will throw if src / dest are in different volumes (#5733) * Java Files.move will throw if src and destination are in different volumes --------- Co-authored-by: Richard Li <742829+rli@users.noreply.github.com> --- .../jetbrains/services/amazonq/lsp/artifacts/LspUtils.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/LspUtils.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/LspUtils.kt index 7724a8c2255..a6846c905a6 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/LspUtils.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/artifacts/LspUtils.kt @@ -7,6 +7,7 @@ import com.intellij.openapi.util.SystemInfo import com.intellij.openapi.util.text.StringUtil import com.intellij.util.io.DigestUtil import com.intellij.util.system.CpuArch +import org.apache.commons.io.FileUtils import software.aws.toolkits.core.utils.ZIP_PROPERTY_POSIX import software.aws.toolkits.core.utils.createParentDirectories import software.aws.toolkits.core.utils.exists @@ -68,7 +69,8 @@ fun getSubFolders(basePath: Path): List = try { fun moveFilesFromSourceToDestination(sourceDir: Path, targetDir: Path) { try { Files.createDirectories(targetDir.parent) - Files.move(sourceDir, targetDir, StandardCopyOption.REPLACE_EXISTING) + // NIO move does not work when copying across mount points (i.e. /tmp is on tmpfs) + FileUtils.moveDirectory(sourceDir.toFile(), targetDir.toFile()) } catch (e: Exception) { throw IllegalStateException("Failed to move files from $sourceDir to $targetDir", e) } From 7b299eda2f780d136ef248519b476d646f3190ab Mon Sep 17 00:00:00 2001 From: Jiatong Li Date: Mon, 19 May 2025 16:23:19 -0700 Subject: [PATCH 2/9] feat(amazonq): add fileUri to FileContext (#5732) In order to coordinate the new aws/language-servers change, GenerateCompletions requests would start expecting the fileUri field to be set. --- .../codewhisperer/editor/CodeWhispererEditorUtil.kt | 8 +++++++- .../services/codewhisperer/model/CodeWhispererModel.kt | 1 + .../codewhisperer/service/CodeWhispererService.kt | 1 + .../codewhisperer/service/CodeWhispererServiceNew.kt | 1 + .../codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt | 2 +- .../codewhisperer/CodeWhispererFileContextProviderTest.kt | 2 ++ .../services/codewhisperer/CodeWhispererServiceTest.kt | 4 +++- .../services/codewhisperer/CodeWhispererTestUtil.kt | 3 ++- .../codegen-resources/codewhispererruntime/service-2.json | 7 +++++++ 9 files changed, 25 insertions(+), 4 deletions(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorUtil.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorUtil.kt index 874a7e25861..61b79766881 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorUtil.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorUtil.kt @@ -32,7 +32,8 @@ object CodeWhispererEditorUtil { val fileName = getFileName(psiFile) val programmingLanguage = psiFile.programmingLanguage() val fileRelativePath = getRelativePathToContentRoot(editor) - return FileContextInfo(caretContext, fileName, programmingLanguage, fileRelativePath) + val fileUri = getFileUri(psiFile) + return FileContextInfo(caretContext, fileName, programmingLanguage, fileRelativePath, fileUri) } fun extractCaretContext(editor: Editor): CaretContext { @@ -73,6 +74,11 @@ object CodeWhispererEditorUtil { private fun getFileName(psiFile: PsiFile): String = psiFile.name.substring(0, psiFile.name.length.coerceAtMost(CodeWhispererConstants.FILENAME_CHARS_LIMIT)) + private fun getFileUri(psiFile: PsiFile): String? = + psiFile.virtualFile?.takeIf { it.isValid }?.let { vFile -> + vFile.url.substring(0, vFile.url.length.coerceAtMost(CodeWhispererConstants.FILENAME_CHARS_LIMIT)) + } + fun getRelativePathToContentRoot(editor: Editor): String? = editor.project?.let { project -> FileDocumentManager.getInstance().getFile(editor.document)?.let { vFile -> diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/model/CodeWhispererModel.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/model/CodeWhispererModel.kt index af2bd28c33e..fdf4c4e7377 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/model/CodeWhispererModel.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/model/CodeWhispererModel.kt @@ -59,6 +59,7 @@ data class FileContextInfo( val filename: String, val programmingLanguage: CodeWhispererProgrammingLanguage, val fileRelativePath: String?, + val fileUri: String?, ) data class SupplementalContextInfo( diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt index 5a548b23895..67ff8112772 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererService.kt @@ -860,6 +860,7 @@ class CodeWhispererService(private val cs: CoroutineScope) : Disposable { .leftFileContent(fileContextInfo.caretContext.leftFileContext) .rightFileContent(fileContextInfo.caretContext.rightFileContext) .filename(fileContextInfo.fileRelativePath ?: fileContextInfo.filename) + .fileUri(fileContextInfo.fileUri) .programmingLanguage(programmingLanguage) .build() val supplementalContexts = supplementalContext?.contents?.map { diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererServiceNew.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererServiceNew.kt index 633a20ac0f9..15003451181 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererServiceNew.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/service/CodeWhispererServiceNew.kt @@ -838,6 +838,7 @@ class CodeWhispererServiceNew(private val cs: CoroutineScope) : Disposable { .leftFileContent(fileContextInfo.caretContext.leftFileContext) .rightFileContent(fileContextInfo.caretContext.rightFileContext) .filename(fileContextInfo.fileRelativePath ?: fileContextInfo.filename) + .fileUri(fileContextInfo.fileUri) .programmingLanguage(programmingLanguage) .build() val supplementalContexts = supplementalContext?.contents?.map { diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt index 057ccfca31a..d181f8053b5 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt @@ -162,7 +162,7 @@ internal class CodeWhispererCodeCoverageTrackerTestPython : CodeWhispererCodeCov fixture.editor, mock(), mock(), - FileContextInfo(mock(), pythonFileName, CodeWhispererPython.INSTANCE, pythonFileName), + FileContextInfo(mock(), pythonFileName, CodeWhispererPython.INSTANCE, pythonFileName, null), runBlocking { async { SupplementalContextInfo( diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererFileContextProviderTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererFileContextProviderTest.kt index 3cdc74dbcfb..588aa7c844b 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererFileContextProviderTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererFileContextProviderTest.kt @@ -109,6 +109,7 @@ class CodeWhispererFileContextProviderTest { ), "Foo.java", CodeWhispererJava.INSTANCE, + "", "" ) @@ -128,6 +129,7 @@ class CodeWhispererFileContextProviderTest { ), "Foo.java", CodeWhispererJava.INSTANCE, + "", "" ) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt index b8d6322500c..46dd2551f15 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt @@ -117,7 +117,8 @@ class CodeWhispererServiceTest { CaretContext(leftFileContext = "", rightFileContext = "public class Main {}", leftContextOnCurrentLine = ""), "main.java", CodeWhispererJava.INSTANCE, - "main.java" + "main.java", + "temp:///src/main.java" ) ) } @@ -240,6 +241,7 @@ private fun CodeWhispererProgrammingLanguage.toSdkModel(): ProgrammingLanguage = private fun FileContextInfo.toSdkModel(): FileContext = FileContext.builder() .filename(fileRelativePath) + .fileUri(fileUri) .programmingLanguage(programmingLanguage.toCodeWhispererRuntimeLanguage().toSdkModel()) .leftFileContent(caretContext.leftFileContext) .rightFileContent(caretContext.rightFileContext) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tstFixtures/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tstFixtures/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt index d9474dde6dc..bd40ed11397 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tstFixtures/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tstFixtures/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt @@ -397,12 +397,13 @@ fun aFileContextInfo(language: CodeWhispererProgrammingLanguage? = null): FileCo val caretContextInfo = CaretContext(aString(), aString(), aString()) val fileName = aString() val fileRelativePath = Paths.get("test", fileName).toString() + val fileUri = "file:///$fileRelativePath" val programmingLanguage = language ?: listOf( CodeWhispererPython.INSTANCE, CodeWhispererJava.INSTANCE ).random() - return FileContextInfo(caretContextInfo, fileName, programmingLanguage, fileRelativePath) + return FileContextInfo(caretContextInfo, fileName, programmingLanguage, fileRelativePath, fileUri) } fun aTriggerType(): CodewhispererTriggerType = diff --git a/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json b/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json index 16805ea2b68..d75fdc527bd 100644 --- a/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json +++ b/plugins/core/sdk-codegen/codegen-resources/codewhispererruntime/service-2.json @@ -1596,9 +1596,16 @@ "leftFileContent":{"shape":"FileContextLeftFileContentString"}, "rightFileContent":{"shape":"FileContextRightFileContentString"}, "filename":{"shape":"FileContextFilenameString"}, + "fileUri": {"shape":"FileContextFileUriString"}, "programmingLanguage":{"shape":"ProgrammingLanguage"} } }, + "FileContextFileUriString": { + "type": "string", + "max": 1024, + "min": 1, + "sensitive": true + }, "FileContextFilenameString":{ "type":"string", "max":1024, From e52d969a4a8499cea71ecaaf8e60bf16b9674f6f Mon Sep 17 00:00:00 2001 From: andrewyuq <89420755+andrewyuq@users.noreply.github.com> Date: Tue, 20 May 2025 01:29:21 -0700 Subject: [PATCH 3/9] feat(amazonq): Add restart mechanism when the flare server process is not running (#5711) * feat(amazonq): Add restart mechanism when the flare server process is not running * address comments * address comments * remove debugging options * fix potential deadlock code * fix unlock error * address comments * remove debug lines --- .../services/amazonq/lsp/AmazonQLspService.kt | 72 ++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt index 21ef074c400..cb33a19b53f 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/AmazonQLspService.kt @@ -24,12 +24,16 @@ import com.intellij.util.net.HttpConfigurable import com.intellij.util.net.JdkProxyProvider import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Deferred +import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.future.asCompletableFuture +import kotlinx.coroutines.isActive +import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock @@ -52,6 +56,7 @@ import org.eclipse.lsp4j.jsonrpc.json.JsonRpcMethod import org.eclipse.lsp4j.jsonrpc.messages.ResponseMessage import org.eclipse.lsp4j.launch.LSPLauncher import org.slf4j.event.Level +import software.aws.toolkits.core.utils.debug import software.aws.toolkits.core.utils.getLogger import software.aws.toolkits.core.utils.info import software.aws.toolkits.core.utils.warn @@ -128,6 +133,9 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS val encryptionManager get() = instance.getCompleted().encryptionManager + private val heartbeatJob: Job + private val restartTimestamps = ArrayDeque() + private val restartMutex = Mutex() // Separate mutex for restart tracking val rawEndpoint get() = instance.getCompleted().rawEndpoint @@ -166,9 +174,50 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS init { instance = start() + + // Initialize heartbeat job + heartbeatJob = cs.launch { + while (isActive) { + delay(5.seconds) // Check every 5 seconds + val shouldLoop = checkConnectionStatus() + if (!shouldLoop) { + break + } + } + } + } + + private suspend fun checkConnectionStatus(): Boolean { + try { + val currentInstance = mutex.withLock { instance }.await() + + // Check if the launcher's Future (startListening) is done + // If it's done, that means the connection has been terminated + if (currentInstance.launcherFuture.isDone) { + LOG.debug { "LSP server connection terminated, checking restart limits" } + val canRestart = checkForRemainingRestartAttempts() + if (!canRestart) { + return false + } + LOG.debug { "Restarting LSP server" } + restart() + } else { + LOG.debug { "LSP server is currently running" } + } + } catch (e: Exception) { + LOG.debug(e) { "Connection status check failed, checking restart limits" } + val canRestart = checkForRemainingRestartAttempts() + if (!canRestart) { + return false + } + LOG.debug { "Restarting LSP server" } + restart() + } + return true } override fun dispose() { + heartbeatJob.cancel() } suspend fun restart() = mutex.withLock { @@ -195,6 +244,25 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS instance = start() } + private suspend fun checkForRemainingRestartAttempts(): Boolean = restartMutex.withLock { + val currentTime = System.currentTimeMillis() + + while (restartTimestamps.isNotEmpty() && + currentTime - restartTimestamps.first() > RESTART_WINDOW_MS + ) { + restartTimestamps.removeFirst() + } + + if (restartTimestamps.size < MAX_RESTARTS) { + restartTimestamps.addLast(currentTime) + return true + } + + LOG.info { "Rate limit reached for LSP server restarts. Stop attempting to restart." } + + return false + } + suspend fun execute(runnable: suspend AmazonQLspService.(AmazonQLanguageServer) -> T): T { val lsp = withTimeout(10.seconds) { val holder = mutex.withLock { instance }.await() @@ -212,6 +280,8 @@ class AmazonQLspService(private val project: Project, private val cs: CoroutineS companion object { private val LOG = getLogger() + private const val MAX_RESTARTS = 5 + private const val RESTART_WINDOW_MS = 3 * 60 * 1000 fun getInstance(project: Project) = project.service() @Deprecated("Easy to accidentally freeze EDT") @@ -241,7 +311,7 @@ private class AmazonQServerInstance(private val project: Project, private val cs get() = launcher.remoteEndpoint @Suppress("ForbiddenVoid") - private val launcherFuture: Future + val launcherFuture: Future private val launcherHandler: KillableProcessHandler val initializeResult: Deferred From 47046c311886a14b0a4bf26626526bf84d0ce758 Mon Sep 17 00:00:00 2001 From: manodnyab <66754471+manodnyab@users.noreply.github.com> Date: Tue, 20 May 2025 09:12:40 -0700 Subject: [PATCH 4/9] fix(amazonq): Fix "Explain" code scan issue (#5712) * Fix code scan issue * refactor code * feedback --- .../services/cwc/commands/ActionRegistrar.kt | 12 ++--- .../codescan/actions/CodeScanQActions.kt | 25 --------- .../actions/ExplainCodeIssueAction.kt | 53 ++++++++++++++++++- .../amazonq/lsp/model/aws/chat/ChatPrompt.kt | 2 +- .../lsp/model/aws/chat/FlareChatCommands.kt | 2 + 5 files changed, 59 insertions(+), 35 deletions(-) delete mode 100644 plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/CodeScanQActions.kt diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/ActionRegistrar.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/ActionRegistrar.kt index d84ef7b3abf..b9a6efd5b48 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/ActionRegistrar.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/ActionRegistrar.kt @@ -11,7 +11,9 @@ import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.runBlocking import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AsyncChatUiListener import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.FlareUiMessage +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GENERIC_COMMAND import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GenericCommandParams +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SEND_TO_PROMPT import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SendToPromptParams import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.TriggerType import software.aws.toolkits.jetbrains.services.amazonq.messages.AmazonQMessage @@ -36,12 +38,12 @@ class ActionRegistrar { val fileContext = contextExtractor.extractContextForTrigger(ExtractionTriggerType.ContextMenu) val codeSelection = "\n```\n${fileContext.focusAreaContext?.codeSelection?.trimIndent()?.trim()}\n```\n" var uiMessage: FlareUiMessage? = null - if (command.verb != "sendToPrompt") { + if (command.verb != SEND_TO_PROMPT) { val params = GenericCommandParams(selection = codeSelection, triggerType = TriggerType.CONTEXT_MENU, genericCommand = command.name) - uiMessage = FlareUiMessage(command = "genericCommand", params = params) + uiMessage = FlareUiMessage(command = GENERIC_COMMAND, params = params) } else { val params = SendToPromptParams(selection = codeSelection, triggerType = TriggerType.CONTEXT_MENU) - uiMessage = FlareUiMessage(command = "sendToPrompt", params = params) + uiMessage = FlareUiMessage(command = SEND_TO_PROMPT, params = params) } AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage) } @@ -49,10 +51,6 @@ class ActionRegistrar { } } - fun reportMessageClick(command: EditorContextCommand, issue: MutableMap, project: Project) { - _messages.tryEmit(CodeScanIssueActionMessage(command, issue, project)) - } - // provide singleton access companion object { val instance = ActionRegistrar() diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/CodeScanQActions.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/CodeScanQActions.kt deleted file mode 100644 index 1dba4220223..00000000000 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/CodeScanQActions.kt +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved. -// SPDX-License-Identifier: Apache-2.0 - -package software.aws.toolkits.jetbrains.services.cwc.commands.codescan.actions - -import com.intellij.openapi.actionSystem.ActionManager -import com.intellij.openapi.actionSystem.AnAction -import com.intellij.openapi.actionSystem.AnActionEvent -import com.intellij.openapi.actionSystem.CommonDataKeys -import com.intellij.openapi.actionSystem.DataKey -import com.intellij.openapi.project.DumbAware -import software.aws.toolkits.jetbrains.services.cwc.commands.ActionRegistrar -import software.aws.toolkits.jetbrains.services.cwc.commands.EditorContextCommand - -open class CodeScanQActions(private val command: EditorContextCommand) : AnAction(), DumbAware { - override fun actionPerformed(e: AnActionEvent) { - val issueDataKey = DataKey.create>("amazonq.codescan.explainissue") - val issueContext = e.getData(issueDataKey) ?: return - val project = e.getData(CommonDataKeys.PROJECT) ?: return - - ActionManager.getInstance().getAction("q.openchat").actionPerformed(e) - - ActionRegistrar.instance.reportMessageClick(command, issueContext, project) - } -} diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/ExplainCodeIssueAction.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/ExplainCodeIssueAction.kt index 48a10d2a381..bf744d4b569 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/ExplainCodeIssueAction.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/codescan/actions/ExplainCodeIssueAction.kt @@ -3,6 +3,55 @@ package software.aws.toolkits.jetbrains.services.cwc.commands.codescan.actions -import software.aws.toolkits.jetbrains.services.cwc.commands.EditorContextCommand +import com.intellij.openapi.actionSystem.ActionManager +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.AnActionEvent +import com.intellij.openapi.actionSystem.DataKey +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.DumbAware +import kotlinx.coroutines.runBlocking +import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.AsyncChatUiListener +import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.FlareUiMessage +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ChatPrompt +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SEND_TO_PROMPT +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SendToPromptParams +import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.TriggerType -class ExplainCodeIssueAction : CodeScanQActions(EditorContextCommand.ExplainCodeScanIssue) +class ExplainCodeIssueAction : AnAction(), DumbAware { + override fun actionPerformed(e: AnActionEvent) { + val issueDataKey = DataKey.create>("amazonq.codescan.explainissue") + val issueContext = e.getData(issueDataKey) ?: return + + ActionManager.getInstance().getAction("q.openchat").actionPerformed(e) + + ApplicationManager.getApplication().executeOnPooledThread { + runBlocking { + // https://github.com/aws/aws-toolkit-vscode/blob/master/packages/amazonq/src/lsp/chat/commands.ts#L30 + val codeSelection = "\n```\n${issueContext["code"]?.trimIndent()?.trim()}\n```\n" + + val prompt = "Explain the issue \n\n " + + "Issue: \"${issueContext["title"]}\" \n" + + "Code: $codeSelection" + + val modelPrompt = "Explain the issue ${issueContext["title"]} \n\n " + + "Issue: \"${issueContext["title"]}\" \n" + + "Description: ${issueContext["description"]} \n" + + "Code: $codeSelection and generate the code demonstrating the fix" + + val params = SendToPromptParams( + selection = codeSelection, + triggerType = TriggerType.CONTEXT_MENU, + prompt = ChatPrompt( + prompt = prompt, + escapedPrompt = modelPrompt, + command = null + ), + autoSubmit = true + ) + + val uiMessage = FlareUiMessage(SEND_TO_PROMPT, params) + AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage) + } + } + } +} diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt index e01c9fdff37..80e39191891 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/ChatPrompt.kt @@ -9,7 +9,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.flareChat.ContextCom data class ChatPrompt( val prompt: String, val escapedPrompt: String, - val command: String, + val command: String?, ) data class SendChatPromptRequest( diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt index 1dc7579b35b..d964435c9eb 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/FlareChatCommands.kt @@ -38,6 +38,7 @@ const val DID_REMOVE_FILE = "aws/didRemoveFileOrDirectory" const val DID_CREATE_DIRECTORY = "aws/didCreateDirectory" const val GET_SERIALIZED_CHAT_REQUEST_METHOD = "aws/chat/getSerializedChat" +const val GENERIC_COMMAND = "genericCommand" const val OPEN_FILE_DIFF = "aws/openFileDiff" const val OPEN_SETTINGS = "openSettings" @@ -47,4 +48,5 @@ const val PROMPT_INPUT_OPTIONS_CHANGE = "aws/chat/promptInputOptionChange" const val SEND_CHAT_COMMAND_PROMPT = "aws/chat/sendChatPrompt" const val SHOW_SAVE_FILE_DIALOG_REQUEST_METHOD = "aws/showSaveFileDialog" const val STOP_CHAT_RESPONSE = "stopChatResponse" +const val SEND_TO_PROMPT = "sendToPrompt" const val TELEMETRY_EVENT = "telemetry/event" From dc82572e0e2c2292884750f003412675019438cf Mon Sep 17 00:00:00 2001 From: manodnyab <66754471+manodnyab@users.noreply.github.com> Date: Tue, 20 May 2025 10:46:44 -0700 Subject: [PATCH 5/9] fix(amazonq): Switch profiles if not selected in Q chat (#5724) * Switch profiles if not selected * fix screen rendereing error * changed name * feedback * disposed * detekt --- .../services/amazonq/QRefreshPanelAction.kt | 1 - .../amazonq/toolwindow/AmazonQToolWindow.kt | 1 - .../lsp/flareChat/ChatCommunicationManager.kt | 20 ++++++++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QRefreshPanelAction.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QRefreshPanelAction.kt index 8b22e156399..84ba01c0e4c 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QRefreshPanelAction.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/QRefreshPanelAction.kt @@ -11,7 +11,6 @@ import com.intellij.openapi.project.DumbAwareAction import com.intellij.util.messages.Topic import software.aws.toolkits.jetbrains.services.amazonq.toolwindow.AmazonQToolWindow import software.aws.toolkits.resources.AmazonQBundle -import software.aws.toolkits.resources.message import java.util.EventListener class QRefreshPanelAction : DumbAwareAction(AmazonQBundle.message("amazonq.refresh.panel"), null, AllIcons.Actions.Refresh) { diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindow.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindow.kt index 7e0cc266c11..5df972bba09 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindow.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindow.kt @@ -24,7 +24,6 @@ class AmazonQToolWindow private constructor( val component get() = chatPanel.component - fun disposeAndRecreate() { Disposer.dispose(chatPanel) chatPanel = AmazonQPanel(project, scope) diff --git a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/ChatCommunicationManager.kt b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/ChatCommunicationManager.kt index 1f00ebc79f8..84a712094b1 100644 --- a/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/ChatCommunicationManager.kt +++ b/plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/flareChat/ChatCommunicationManager.kt @@ -25,6 +25,7 @@ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.CHAT_ import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.ErrorParams import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.GetSerializedChatResult import software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat.SEND_CHAT_COMMAND_PROMPT +import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileDialog import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileManager import software.aws.toolkits.jetbrains.services.amazonq.profile.QRegionProfileSelectedListener import java.util.UUID @@ -140,19 +141,28 @@ class ChatCommunicationManager(private val cs: CoroutineScope) { val incomingType = params.authFollowupType val connectionManager = ToolkitConnectionManager.getInstance(project) try { - connectionManager.activeConnectionForFeature(QConnection.getInstance())?.let { - reauthConnectionIfNeeded(project, it, isReAuth = true) - } when (incomingType) { AuthFollowupType.USE_SUPPORTED_AUTH -> { - project.messageBus.syncPublisher(QRegionProfileSelectedListener.TOPIC) - .onProfileSelected(project, QRegionProfileManager.getInstance().activeProfile(project)) + val activeProfile = QRegionProfileManager.getInstance().activeProfile(project) + if (activeProfile != null) { + project.messageBus.syncPublisher(QRegionProfileSelectedListener.TOPIC) + .onProfileSelected(project, QRegionProfileManager.getInstance().activeProfile(project)) + } else { + QRegionProfileDialog( + project, + selectedProfile = null + ).show() + } + return } AuthFollowupType.RE_AUTH, AuthFollowupType.MISSING_SCOPES, AuthFollowupType.FULL_AUTH, -> { + connectionManager.activeConnectionForFeature(QConnection.getInstance())?.let { + reauthConnectionIfNeeded(project, it, isReAuth = true) + } return } else -> { From 309ef00df75fa8ea121969ff6fcf6715bb342ac2 Mon Sep 17 00:00:00 2001 From: manodnyab <66754471+manodnyab@users.noreply.github.com> Date: Tue, 20 May 2025 11:18:46 -0700 Subject: [PATCH 6/9] deps(mynah): Update disclaimer text (#5740) --- plugins/amazonq/mynah-ui/src/mynah-ui/ui/texts/disclaimer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/texts/disclaimer.ts b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/texts/disclaimer.ts index 4bf62164a8b..4e06601420b 100644 --- a/plugins/amazonq/mynah-ui/src/mynah-ui/ui/texts/disclaimer.ts +++ b/plugins/amazonq/mynah-ui/src/mynah-ui/ui/texts/disclaimer.ts @@ -8,7 +8,7 @@ import { ChatItem, MynahIcons } from '@aws/mynah-ui-chat' export const disclaimerAcknowledgeButtonId = 'amazonq-disclaimer-acknowledge-button-id' export const disclaimerCard: Partial = { messageId: 'amazonq-disclaimer-card', - body: 'Amazon Q Developer uses generative AI. You may need to verify responses. See the [AWS Responsible AI Policy](https://aws.amazon.com/machine-learning/responsible-ai/policy/). Amazon Q Developer processes data across all US Regions. See [here](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/cross-region-inference.html) for more info. Amazon Q may retain chats to provide and maintain the service.', + body: 'Amazon Q Developer uses generative AI. You may need to verify responses. See the [AWS Responsible AI Policy](https://aws.amazon.com/machine-learning/responsible-ai/policy/). Amazon Q may retain chats to provide and maintain the service. For information on the AWS Regions where Amazon Q may perform inference, see [the documentation](https://docs.aws.amazon.com/amazonq/latest/qdeveloper-ug/cross-region-processing.html#cross-region-inference).', buttons: [ { text: 'Acknowledge', From 3828011187936cf8cf9486bad8e99abbd70ed39a Mon Sep 17 00:00:00 2001 From: Will Lo <96078566+Will-ShaoHua@users.noreply.github.com> Date: Tue, 20 May 2025 11:20:11 -0700 Subject: [PATCH 7/9] refactor(amazonq): remove redunant panel re-creation (#5729) --- .../amazonq/toolwindow/AmazonQToolWindowFactory.kt | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindowFactory.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindowFactory.kt index a61fa43855b..3ae16dd96c6 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindowFactory.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/toolwindow/AmazonQToolWindowFactory.kt @@ -66,7 +66,7 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware { ToolkitConnectionManager.getInstance(project).activeConnectionForFeature(QConnection.getInstance())?.let { qConn -> openMeetQPage(project) } - prepareChatContent(project, qPanel) + preparePanelContent(project, qPanel) } } ) @@ -75,7 +75,7 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware { RefreshQChatPanelButtonPressedListener.TOPIC, object : RefreshQChatPanelButtonPressedListener { override fun onRefresh() { - prepareChatContent(project, qPanel) + preparePanelContent(project, qPanel) } } ) @@ -85,8 +85,7 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware { object : BearerTokenProviderListener { override fun onChange(providerId: String, newScopes: List?) { if (ToolkitConnectionManager.getInstance(project).connectionStateForFeature(QConnection.getInstance()) == BearerTokenAuthState.AUTHORIZED) { - AmazonQToolWindow.getInstance(project).disposeAndRecreate() - prepareChatContent(project, qPanel) + preparePanelContent(project, qPanel) } } } @@ -98,13 +97,12 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware { // note we name myProject intentionally ow it will shadow the "project" provided by the IDE override fun onProfileSelected(myProject: Project, profile: QRegionProfile?) { if (project.isDisposed) return - AmazonQToolWindow.getInstance(project).disposeAndRecreate() - prepareChatContent(project, qPanel) + preparePanelContent(project, qPanel) } } ) - prepareChatContent(project, qPanel) + preparePanelContent(project, qPanel) val content = contentManager.factory.createContent(mainPanel, null, false).also { it.isCloseable = true @@ -114,7 +112,7 @@ class AmazonQToolWindowFactory : ToolWindowFactory, DumbAware { contentManager.addContent(content) } - private fun prepareChatContent( + private fun preparePanelContent( project: Project, qPanel: Wrapper, ) { From aa5ff7a4474daf41f1d076fa190046a6dc31050e Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 20 May 2025 12:59:31 -0700 Subject: [PATCH 8/9] build/lint --- .../codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt | 0 .../jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt | 2 +- .../jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt | 0 3 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt delete mode 100644 plugins/amazonq/codewhisperer/jetbrains-community/tstFixtures/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererCodeCoverageTrackerTest.kt deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt index 6b2f8542852..fd6b9a0c49f 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt @@ -127,7 +127,7 @@ fun aFileContextInfo(language: CodeWhispererProgrammingLanguage? = null): FileCo CodeWhispererJava.INSTANCE ).random() - return FileContextInfo(caretContextInfo, fileName, programmingLanguage, fileRelativePath) + return FileContextInfo(caretContextInfo, fileName, programmingLanguage, fileRelativePath, null) } fun aTriggerType(): CodewhispererTriggerType = diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tstFixtures/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tstFixtures/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererTestUtil.kt deleted file mode 100644 index e69de29bb2d..00000000000 From 5e80c65b30fefadcf8facd20fbbcbc5e5ba27ac2 Mon Sep 17 00:00:00 2001 From: Richard Li Date: Tue, 20 May 2025 14:24:32 -0700 Subject: [PATCH 9/9] tst --- .../services/codewhisperer/CodeWhispererServiceTest.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt index 99e5d56efe8..37015577346 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/tst/software/aws/toolkits/jetbrains/services/codewhisperer/CodeWhispererServiceTest.kt @@ -75,7 +75,7 @@ class CodeWhispererServiceTest : CodeWhispererTestBase() { "main.java", CodeWhispererJava.INSTANCE, "main.java", - "temp:///src/main.java" + file.virtualFile.url ) ) }