Skip to content

fix(amazonq): Support chat quick action commands #5700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 9, 2025
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
// Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

@file:Suppress("BannedImports")
package software.aws.toolkits.jetbrains.services.cwc.commands

import com.intellij.openapi.project.Project
import kotlinx.coroutines.flow.MutableSharedFlow
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.GenericCommandParams
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
import software.aws.toolkits.jetbrains.services.cwc.editor.context.ActiveFileContextExtractor
import software.aws.toolkits.jetbrains.services.cwc.editor.context.ExtractionTriggerType

// Register Editor Actions in the Editor Context Menu
class ActionRegistrar {
Expand All @@ -15,7 +23,26 @@
val flow = _messages.asSharedFlow()

fun reportMessageClick(command: EditorContextCommand, project: Project) {
_messages.tryEmit(ContextMenuActionMessage(command, project))
if (command == EditorContextCommand.GenerateUnitTests) {
// pre-existing old chat code path
_messages.tryEmit(ContextMenuActionMessage(command, project))
} else {
// new agentic chat route
runBlocking {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will block edt

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well if I mark reportMessageClick as suspend it would require me to mark other functions as suspend.

Should I launch with coroutine?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i really don't like ActionRegistrar
let's just do

ApplicationManager.getApplication().executeOnPooledThread {
    runBlocking {
...
    }
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe try this https://youtrack.jetbrains.com/issue/IJPL-371/Provide-currentThreadCoroutineScope-inside-AnActionactionPerformed

you can request a coroutine scope from the platform in the action system

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe like

        currentThreadCoroutineScope().launch(getCoroutineBgContext()) {
            ActionRegistrar.instance.reportMessageClick
        }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can skip ActionRegistrar for these and handle it in custom action

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

skip ActionRegistrar for these and handle it in custom action
This would require more lines of code.

val contextExtractor = ActiveFileContextExtractor.create(fqnWebviewAdapter = null, project = project)
val fileContext = contextExtractor.extractContextForTrigger(ExtractionTriggerType.ContextMenu)
val codeSelection = "\n```\n${fileContext.focusAreaContext?.codeSelection?.trimIndent()?.trim()}\n```\n"
var uiMessage: FlareUiMessage? = null

Check warning on line 35 in plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/cwc/commands/ActionRegistrar.kt

View workflow job for this annotation

GitHub Actions / qodana

Variable initializer is redundant

Initializer is redundant
if (command.verb != "sendToPrompt") {
val params = GenericCommandParams(selection = codeSelection, triggerType = TriggerType.CONTEXT_MENU, genericCommand = command.name)
uiMessage = FlareUiMessage(command = "genericCommand", params = params)
} else {
val params = SendToPromptParams(selection = codeSelection, triggerType = TriggerType.CONTEXT_MENU)
uiMessage = FlareUiMessage(command = "sendToPrompt", params = params)
}
AsyncChatUiListener.notifyPartialMessageUpdate(uiMessage)
}
}
}

fun reportMessageClick(command: EditorContextCommand, issue: MutableMap<String, String>, project: Project) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ enum class EditorContextCommand(
actionId = "aws.amazonq.generateUnitTests",
),
SendToPrompt(
verb = "SendToPrompt",
verb = "sendToPrompt",
actionId = "aws.amazonq.sendToPrompt",
),
ExplainCodeScanIssue(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -289,28 +289,11 @@ class ChatController private constructor(
if (message.project != context.project) {
return
}
val fileContext = contextExtractor.extractContextForTrigger(ExtractionTriggerType.ContextMenu)
val triggerId = UUID.randomUUID().toString()
val codeSelection = "\n```\n${fileContext.focusAreaContext?.codeSelection?.trimIndent()?.trim()}\n```\n"

if (message.command == EditorContextCommand.SendToPrompt) {
messagePublisher.publish(
EditorContextCommandMessage(
message = codeSelection,
command = message.command.actionId,
triggerId = triggerId,
),
)
return
}

if (message.command == EditorContextCommand.GenerateUnitTests) {
// Publish an event to "codetest" tab with command as "test" and type as "addAnswer"
val messageToPublish = TestCommandMessage()
context.messagesFromAppToUi.publish(messageToPublish)
} else {
// Create prompt
val prompt = "${message.command} the following part of my code for me: $codeSelection"
processPromptActions(prompt, message, triggerId, fileContext)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

package software.aws.toolkits.jetbrains.services.amazonq.lsp.model.aws.chat

// https://github.com/aws/language-server-runtimes/blob/main/chat-client-ui-types/src/uiContracts.ts#L27
enum class TriggerType(val value: String) {
HOTKEYS("hotkeys"),

Check warning on line 8 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/GenericCommandParams.kt

View workflow job for this annotation

GitHub Actions / qodana

Unused symbol

Class "HOTKEYS" is never used
CLICK("click"),

Check warning on line 9 in plugins/amazonq/shared/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonq/lsp/model/aws/chat/GenericCommandParams.kt

View workflow job for this annotation

GitHub Actions / qodana

Unused symbol

Class "CLICK" is never used
CONTEXT_MENU("contextMenu"),
}

data class GenericCommandParams(
val tabId: String? = null,
val selection: String,
val triggerType: TriggerType,
val genericCommand: String,
)

// https://github.com/aws/language-server-runtimes/blob/b7c4718b9bd84e08e72b992da5d699549af9f115/chat-client-ui-types/src/uiContracts.ts#L67
data class SendToPromptParams(
val selection: String,
val triggerType: TriggerType,
val prompt: ChatPrompt? = null,
val autoSubmit: Boolean? = null,
)
Loading