Skip to content

Commit db7b14b

Browse files
committed
Replace old actions with new actions that have configurable key bindings
1. Accept (Tab) 2. Force Accept (Opt + Tab/Opt + Enter) 3. Navigate to Previous (Opt + [) 4. Navigate to Next (Opt + ])
1 parent fa3dcf2 commit db7b14b

21 files changed

+242
-151
lines changed

plugins/amazonq/codewhisperer/jetbrains-community/resources/META-INF/plugin-codewhisperer.xml

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,31 @@
8181
</group>
8282

8383
<action class="software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererRecommendationAction"
84-
text="Show Code Suggestions">
84+
text="Invoke Amazon Q Inline Suggestions">
8585
<keyboard-shortcut keymap="$default" first-keystroke="alt C"/>
8686
</action>
87+
<action id="codewhisperer.inline.navigate.previous"
88+
class="software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererNavigatePrevAction"
89+
text="Navigate to Previous Inline Suggestion" description="Navigate to previous inline suggestion">
90+
<keyboard-shortcut keymap="$default" first-keystroke="alt OPEN_BRACKET"/>
91+
</action>
92+
<action id="codewhisperer.inline.navigate.next"
93+
class="software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererNavigateNextAction"
94+
text="Navigate to Next Inline Suggestion" description="Navigate to next inline suggestion">
95+
<keyboard-shortcut keymap="$default" first-keystroke="alt CLOSE_BRACKET"/>
96+
</action>
97+
<action id="codewhisperer.inline.accept"
98+
class="software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererAcceptAction"
99+
text="Accept the Current Inline Suggestion" description="Accept the current inline suggestions">
100+
<keyboard-shortcut keymap="$default" first-keystroke="TAB"/>
101+
</action>
102+
<action id="codewhisperer.inline.force.accept"
103+
class="software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererForceAcceptAction"
104+
text="Force Accept the Current Inline Suggestion" description="Force accept the current inline suggestion">
105+
<keyboard-shortcut keymap="$default" first-keystroke="alt TAB"/>
106+
<keyboard-shortcut keymap="$default" first-keystroke="alt ENTER"/>
107+
</action>
108+
87109
<group id="aws.toolkit.codewhisperer.toolbar.security">
88110
<action
89111
id="codewhisperer.toolbar.security.scan"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.codewhisperer.actions
5+
6+
import com.intellij.openapi.actionSystem.ActionUpdateThread
7+
import com.intellij.openapi.actionSystem.AnAction
8+
import com.intellij.openapi.actionSystem.AnActionEvent
9+
import com.intellij.openapi.actionSystem.CommonDataKeys
10+
import com.intellij.openapi.application.ApplicationManager
11+
import com.intellij.openapi.project.DumbAware
12+
import software.aws.toolkits.jetbrains.services.codewhisperer.model.SessionContext
13+
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager
14+
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
15+
import software.aws.toolkits.resources.message
16+
17+
open class CodeWhispererAcceptAction(title: String = message("codewhisperer.inline.accept")) : AnAction(title), DumbAware {
18+
var sessionContext: SessionContext? = null
19+
20+
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
21+
22+
override fun update(e: AnActionEvent) {
23+
e.presentation.isEnabled = e.project != null && e.getData(CommonDataKeys.EDITOR) != null &&
24+
CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()
25+
}
26+
27+
override fun actionPerformed(e: AnActionEvent) {
28+
val sessionContext = sessionContext ?: return
29+
if (!CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()) return
30+
ApplicationManager.getApplication().messageBus.syncPublisher(
31+
CodeWhispererPopupManager.CODEWHISPERER_USER_ACTION_PERFORMED
32+
).beforeAccept(sessionContext)
33+
}
34+
}

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/actions/CodeWhispererActionPromoter.kt

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,38 +3,50 @@
33

44
package software.aws.toolkits.jetbrains.services.codewhisperer.actions
55

6+
import com.intellij.codeInsight.lookup.impl.actions.ChooseItemAction
67
import com.intellij.openapi.actionSystem.ActionPromoter
78
import com.intellij.openapi.actionSystem.AnAction
89
import com.intellij.openapi.actionSystem.DataContext
9-
import com.intellij.openapi.editor.actionSystem.EditorAction
10-
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupLeftArrowHandler
11-
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupRightArrowHandler
12-
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupTabHandler
10+
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
1311

1412
class CodeWhispererActionPromoter : ActionPromoter {
1513
override fun promote(actions: MutableList<out AnAction>, context: DataContext): MutableList<AnAction> {
1614
val results = actions.toMutableList()
15+
if (!CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()) return results
16+
1717
results.sortWith { a, b ->
18-
if (isCodeWhispererPopupAction(a)) {
18+
if (isCodeWhispererForceAction(a)) {
19+
return@sortWith -1
20+
} else if (isCodeWhispererForceAction(b)) {
21+
return@sortWith 1
22+
}
23+
24+
if (a is ChooseItemAction) {
1925
return@sortWith -1
20-
} else if (isCodeWhispererPopupAction(b)) {
26+
} else if (b is ChooseItemAction) {
2127
return@sortWith 1
22-
} else {
23-
0
2428
}
29+
30+
if (isCodeWhispererAcceptAction(a)) {
31+
return@sortWith -1
32+
} else if (isCodeWhispererAcceptAction(b)) {
33+
return@sortWith 1
34+
}
35+
36+
0
2537
}
2638
return results
2739
}
2840

2941
private fun isCodeWhispererAcceptAction(action: AnAction): Boolean =
30-
action is EditorAction && action.handler is CodeWhispererPopupTabHandler
42+
action is CodeWhispererAcceptAction
43+
44+
private fun isCodeWhispererForceAcceptAction(action: AnAction): Boolean =
45+
action is CodeWhispererForceAcceptAction
3146

3247
private fun isCodeWhispererNavigateAction(action: AnAction): Boolean =
33-
action is EditorAction && (
34-
action.handler is CodeWhispererPopupRightArrowHandler ||
35-
action.handler is CodeWhispererPopupLeftArrowHandler
36-
)
48+
action is CodeWhispererNavigateNextAction || action is CodeWhispererNavigatePrevAction
3749

38-
private fun isCodeWhispererPopupAction(action: AnAction): Boolean =
39-
isCodeWhispererAcceptAction(action) || isCodeWhispererNavigateAction(action)
50+
private fun isCodeWhispererForceAction(action: AnAction): Boolean =
51+
isCodeWhispererForceAcceptAction(action) || isCodeWhispererNavigateAction(action)
4052
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.codewhisperer.actions
5+
6+
import software.aws.toolkits.resources.message
7+
8+
// A same accept action but different key shortcut and different promoter logic
9+
class CodeWhispererForceAcceptAction(title: String = message("codewhisperer.inline.force.accept")) : CodeWhispererAcceptAction(title)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.codewhisperer.actions
5+
6+
import com.intellij.openapi.actionSystem.ActionUpdateThread
7+
import com.intellij.openapi.actionSystem.AnAction
8+
import com.intellij.openapi.actionSystem.AnActionEvent
9+
import com.intellij.openapi.actionSystem.CommonDataKeys
10+
import com.intellij.openapi.application.ApplicationManager
11+
import com.intellij.openapi.project.DumbAware
12+
import software.aws.toolkits.jetbrains.services.codewhisperer.model.SessionContext
13+
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager
14+
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
15+
import software.aws.toolkits.resources.message
16+
17+
class CodeWhispererNavigateNextAction : AnAction(message("codewhisperer.inline.navigate.next")), DumbAware {
18+
var sessionContext: SessionContext? = null
19+
20+
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
21+
22+
override fun update(e: AnActionEvent) {
23+
e.presentation.isEnabled = e.project != null && e.getData(CommonDataKeys.EDITOR) != null &&
24+
CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()
25+
}
26+
27+
override fun actionPerformed(e: AnActionEvent) {
28+
val sessionContext = sessionContext ?: return
29+
if (!CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()) return
30+
ApplicationManager.getApplication().messageBus.syncPublisher(
31+
CodeWhispererPopupManager.CODEWHISPERER_USER_ACTION_PERFORMED
32+
).navigateNext(sessionContext)
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.codewhisperer.actions
5+
6+
import com.intellij.openapi.actionSystem.ActionUpdateThread
7+
import com.intellij.openapi.actionSystem.AnAction
8+
import com.intellij.openapi.actionSystem.AnActionEvent
9+
import com.intellij.openapi.actionSystem.CommonDataKeys
10+
import com.intellij.openapi.application.ApplicationManager
11+
import com.intellij.openapi.project.DumbAware
12+
import software.aws.toolkits.jetbrains.services.codewhisperer.model.SessionContext
13+
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager
14+
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
15+
import software.aws.toolkits.resources.message
16+
17+
class CodeWhispererNavigatePrevAction : AnAction(message("codewhisperer.inline.navigate.previous")), DumbAware {
18+
var sessionContext: SessionContext? = null
19+
20+
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT
21+
22+
override fun update(e: AnActionEvent) {
23+
e.presentation.isEnabled = e.project != null && e.getData(CommonDataKeys.EDITOR) != null &&
24+
CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()
25+
}
26+
27+
override fun actionPerformed(e: AnActionEvent) {
28+
val sessionContext = sessionContext ?: return
29+
if (!CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()) return
30+
ApplicationManager.getApplication().messageBus.syncPublisher(
31+
CodeWhispererPopupManager.CODEWHISPERER_USER_ACTION_PERFORMED
32+
).navigatePrevious(sessionContext)
33+
}
34+
}

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/actions/ConnectWithAwsToContinueActionWarn.kt

Lines changed: 0 additions & 32 deletions
This file was deleted.

plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/popup/CodeWhispererPopupManager.kt

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@ package software.aws.toolkits.jetbrains.services.codewhisperer.popup
66
import com.intellij.codeInsight.hint.ParameterInfoController
77
import com.intellij.codeInsight.lookup.LookupManager
88
import com.intellij.idea.AppMode
9+
import com.intellij.openapi.actionSystem.ActionManager
910
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_BACKSPACE
1011
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_ENTER
12+
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_ESCAPE
1113
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_MOVE_CARET_LEFT
1214
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_MOVE_CARET_RIGHT
1315
import com.intellij.openapi.actionSystem.IdeActions.ACTION_EDITOR_TAB
@@ -45,6 +47,10 @@ import software.amazon.awssdk.services.codewhispererruntime.model.Import
4547
import software.amazon.awssdk.services.codewhispererruntime.model.Reference
4648
import software.aws.toolkits.core.utils.debug
4749
import software.aws.toolkits.core.utils.getLogger
50+
import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererAcceptAction
51+
import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererForceAcceptAction
52+
import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererNavigateNextAction
53+
import software.aws.toolkits.jetbrains.services.codewhisperer.actions.CodeWhispererNavigatePrevAction
4854
import software.aws.toolkits.jetbrains.services.codewhisperer.editor.CodeWhispererEditorManager
4955
import software.aws.toolkits.jetbrains.services.codewhisperer.layout.CodeWhispererLayoutConfig.addHorizontalGlue
5056
import software.aws.toolkits.jetbrains.services.codewhisperer.layout.CodeWhispererLayoutConfig.horizontalPanelConstraints
@@ -55,8 +61,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.model.SessionConte
5561
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererEditorActionHandler
5662
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupBackspaceHandler
5763
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupEnterHandler
58-
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupLeftArrowHandler
59-
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupRightArrowHandler
64+
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupEscHandler
6065
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupTabHandler
6166
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupTypedHandler
6267
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.listeners.CodeWhispererAcceptButtonActionListener
@@ -455,41 +460,52 @@ class CodeWhispererPopupManager {
455460
)
456461
}
457462

458-
private fun addButtonActionListeners(states: InvocationContext) {
459-
popupComponents.prevButton.addButtonActionListener(CodeWhispererPrevButtonActionListener(states))
460-
popupComponents.nextButton.addButtonActionListener(CodeWhispererNextButtonActionListener(states))
461-
popupComponents.acceptButton.addButtonActionListener(CodeWhispererAcceptButtonActionListener(states))
463+
private fun addButtonActionListeners(sessionContext: SessionContext) {
464+
popupComponents.prevButton.addButtonActionListener(CodeWhispererPrevButtonActionListener(sessionContext), sessionContext)
465+
popupComponents.nextButton.addButtonActionListener(CodeWhispererNextButtonActionListener(sessionContext), sessionContext)
466+
popupComponents.acceptButton.addButtonActionListener(CodeWhispererAcceptButtonActionListener(sessionContext), sessionContext)
462467
}
463468

464-
private fun JButton.addButtonActionListener(listener: CodeWhispererActionListener) {
469+
private fun JButton.addButtonActionListener(listener: CodeWhispererActionListener, sessionContext: SessionContext) {
465470
this.addActionListener(listener)
466-
Disposer.register(listener.states) { this.removeActionListener(listener) }
471+
Disposer.register(sessionContext) { this.removeActionListener(listener) }
467472
}
468473

469-
private fun setPopupActionHandlers(states: InvocationContext) {
474+
private fun setPopupActionHandlers(sessionContext: SessionContext) {
470475
val actionManager = EditorActionManager.getInstance()
471-
setPopupTypedHandler(CodeWhispererPopupTypedHandler(TypedAction.getInstance().rawHandler, states))
472-
setPopupActionHandler(ACTION_EDITOR_TAB, CodeWhispererPopupTabHandler(states))
473-
setPopupActionHandler(ACTION_EDITOR_MOVE_CARET_LEFT, CodeWhispererPopupLeftArrowHandler(states))
474-
setPopupActionHandler(ACTION_EDITOR_MOVE_CARET_RIGHT, CodeWhispererPopupRightArrowHandler(states))
476+
477+
// TODO: find a better way to pass in the local sessionContext for the handler to know the session state
478+
val prevAction = ActionManager.getInstance().getAction("codewhisperer.inline.navigate.previous") as CodeWhispererNavigatePrevAction
479+
prevAction.sessionContext = sessionContext
480+
val nextAction = ActionManager.getInstance().getAction("codewhisperer.inline.navigate.next") as CodeWhispererNavigateNextAction
481+
nextAction.sessionContext = sessionContext
482+
val acceptAction = ActionManager.getInstance().getAction("codewhisperer.inline.accept") as CodeWhispererAcceptAction
483+
acceptAction.sessionContext = sessionContext
484+
val forceAcceptAction = ActionManager.getInstance().getAction("codewhisperer.inline.force.accept") as CodeWhispererForceAcceptAction
485+
forceAcceptAction.sessionContext = sessionContext
486+
487+
setPopupTypedHandler(CodeWhispererPopupTypedHandler(TypedAction.getInstance().rawHandler, sessionContext), sessionContext)
488+
setPopupActionHandler(ACTION_EDITOR_ESCAPE, CodeWhispererPopupEscHandler(sessionContext), sessionContext)
475489
setPopupActionHandler(
476490
ACTION_EDITOR_ENTER,
477-
CodeWhispererPopupEnterHandler(actionManager.getActionHandler(ACTION_EDITOR_ENTER), states)
491+
CodeWhispererPopupEnterHandler(actionManager.getActionHandler(ACTION_EDITOR_ENTER), sessionContext),
492+
sessionContext
478493
)
479494
setPopupActionHandler(
480495
ACTION_EDITOR_BACKSPACE,
481-
CodeWhispererPopupBackspaceHandler(actionManager.getActionHandler(ACTION_EDITOR_BACKSPACE), states)
496+
CodeWhispererPopupBackspaceHandler(actionManager.getActionHandler(ACTION_EDITOR_BACKSPACE), sessionContext),
497+
sessionContext
482498
)
483499
}
484500

485-
private fun setPopupTypedHandler(newHandler: CodeWhispererPopupTypedHandler) {
501+
private fun setPopupTypedHandler(newHandler: CodeWhispererPopupTypedHandler, sessionContext: SessionContext) {
486502
val oldTypedHandler = TypedAction.getInstance().setupRawHandler(newHandler)
487-
Disposer.register(newHandler.states) { TypedAction.getInstance().setupRawHandler(oldTypedHandler) }
503+
Disposer.register(sessionContext) { TypedAction.getInstance().setupRawHandler(oldTypedHandler) }
488504
}
489505

490-
private fun setPopupActionHandler(id: String, newHandler: CodeWhispererEditorActionHandler) {
506+
private fun setPopupActionHandler(id: String, newHandler: CodeWhispererEditorActionHandler, sessionContext: SessionContext) {
491507
val oldHandler = EditorActionManager.getInstance().setActionHandler(id, newHandler)
492-
Disposer.register(newHandler.states) { EditorActionManager.getInstance().setActionHandler(id, oldHandler) }
508+
Disposer.register(sessionContext) { EditorActionManager.getInstance().setActionHandler(id, oldHandler) }
493509
}
494510

495511
private fun addComponentListeners(states: InvocationContext) {

0 commit comments

Comments
 (0)