Skip to content
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

Add configurable key shortcuts for inline and make JB and Q suggestions co-exist #5270

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,23 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.DumbAware
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatusNew
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererServiceNew
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
import software.aws.toolkits.resources.message

open class CodeWhispererAcceptAction(title: String = message("codewhisperer.inline.accept")) : AnAction(title), DumbAware {
override fun getActionUpdateThread(): ActionUpdateThread = ActionUpdateThread.EDT

override fun update(e: AnActionEvent) {
e.presentation.isEnabled = e.project != null && e.getData(CommonDataKeys.EDITOR) != null &&
CodeWhispererInvocationStatusNew.getInstance().isDisplaySessionActive()
CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()
}

override fun actionPerformed(e: AnActionEvent) {
val sessionContext = e.project?.getUserData(CodeWhispererServiceNew.KEY_SESSION_CONTEXT) ?: return
if (!CodeWhispererInvocationStatusNew.getInstance().isDisplaySessionActive()) return
val states = e.project?.getUserData(CodeWhispererPopupManager.KEY_INVOCATION_CONTEXT) ?: return
if (!CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()) return
ApplicationManager.getApplication().messageBus.syncPublisher(
CodeWhispererPopupManager.CODEWHISPERER_USER_ACTION_PERFORMED
).beforeAccept(sessionContext)
).beforeAccept(states, CodeWhispererPopupManager.getInstance().sessionContext)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,30 @@ import com.intellij.codeInsight.lookup.impl.actions.ChooseItemAction
import com.intellij.openapi.actionSystem.ActionPromoter
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.DataContext
import com.intellij.openapi.editor.actionSystem.EditorAction
import software.aws.toolkits.jetbrains.services.amazonq.CodeWhispererFeatureConfigService
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupLeftArrowHandler
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupRightArrowHandler
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.handlers.CodeWhispererPopupTabHandler
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatusNew
import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings

class CodeWhispererActionPromoter : ActionPromoter {
override fun promote(actions: MutableList<out AnAction>, context: DataContext): MutableList<AnAction> {
if (CodeWhispererFeatureConfigService.getInstance().getNewAutoTriggerUX()) {
val results = actions.toMutableList()
if (!CodeWhispererInvocationStatusNew.getInstance().isDisplaySessionActive()) return results
val results = actions.toMutableList()
if (!CodeWhispererInvocationStatusNew.getInstance().isDisplaySessionActive() &&
!CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()
) {
return results
}

results.sortWith { a, b ->
if (isCodeWhispererForceAction(a)) {
return@sortWith -1
} else if (isCodeWhispererForceAction(b)) {
return@sortWith 1
}

results.sortWith { a, b ->
if (isCodeWhispererForceAction(a)) {
if (CodeWhispererSettings.getInstance().isQPrioritizedForTabAccept()) {
if (isCodeWhispererAcceptAction(a)) {
return@sortWith -1
} else if (isCodeWhispererForceAction(b)) {
} else if (isCodeWhispererAcceptAction(b)) {
return@sortWith 1
}

Expand All @@ -32,52 +39,32 @@ class CodeWhispererActionPromoter : ActionPromoter {
} else if (b is ChooseItemAction) {
return@sortWith 1
}
} else {
if (a is ChooseItemAction) {
return@sortWith -1
} else if (b is ChooseItemAction) {
return@sortWith 1
}

if (isCodeWhispererAcceptAction(a)) {
return@sortWith -1
} else if (isCodeWhispererAcceptAction(b)) {
return@sortWith 1
}

0
}
return results
}
val results = actions.toMutableList()
results.sortWith { a, b ->
if (isCodeWhispererPopupAction(a)) {
return@sortWith -1
} else if (isCodeWhispererPopupAction(b)) {
return@sortWith 1
} else {
0
}

0
}
return results
}

private fun isCodeWhispererAcceptAction(action: AnAction): Boolean =
if (CodeWhispererFeatureConfigService.getInstance().getNewAutoTriggerUX()) {
action is CodeWhispererAcceptAction
} else {
action is EditorAction && action.handler is CodeWhispererPopupTabHandler
}
private fun isCodeWhispererAcceptAction(action: AnAction): Boolean = action is CodeWhispererAcceptAction

private fun isCodeWhispererForceAcceptAction(action: AnAction): Boolean =
action is CodeWhispererForceAcceptAction

private fun isCodeWhispererNavigateAction(action: AnAction): Boolean =
if (CodeWhispererFeatureConfigService.getInstance().getNewAutoTriggerUX()) {
action is CodeWhispererNavigateNextAction || action is CodeWhispererNavigatePrevAction
} else {
action is EditorAction && (
action.handler is CodeWhispererPopupRightArrowHandler ||
action.handler is CodeWhispererPopupLeftArrowHandler
)
}

private fun isCodeWhispererPopupAction(action: AnAction): Boolean =
isCodeWhispererAcceptAction(action) || isCodeWhispererNavigateAction(action)
action is CodeWhispererNavigateNextAction || action is CodeWhispererNavigatePrevAction

private fun isCodeWhispererForceAction(action: AnAction): Boolean =
isCodeWhispererForceAcceptAction(action) || isCodeWhispererNavigateAction(action)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.DumbAware
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatusNew
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererServiceNew
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
import software.aws.toolkits.resources.message

class CodeWhispererNavigateNextAction : AnAction(message("codewhisperer.inline.navigate.next")), DumbAware {
Expand All @@ -20,14 +19,14 @@ class CodeWhispererNavigateNextAction : AnAction(message("codewhisperer.inline.n
override fun update(e: AnActionEvent) {
e.presentation.isEnabled = e.project != null &&
e.getData(CommonDataKeys.EDITOR) != null &&
CodeWhispererInvocationStatusNew.getInstance().isDisplaySessionActive()
CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()
}

override fun actionPerformed(e: AnActionEvent) {
val sessionContext = e.project?.getUserData(CodeWhispererServiceNew.KEY_SESSION_CONTEXT) ?: return
if (!CodeWhispererInvocationStatusNew.getInstance().isDisplaySessionActive()) return
val states = e.project?.getUserData(CodeWhispererPopupManager.KEY_INVOCATION_CONTEXT) ?: return
if (!CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()) return
ApplicationManager.getApplication().messageBus.syncPublisher(
CodeWhispererPopupManager.CODEWHISPERER_USER_ACTION_PERFORMED
).navigateNext(sessionContext)
).navigateNext(states)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import com.intellij.openapi.actionSystem.CommonDataKeys
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.project.DumbAware
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatusNew
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererServiceNew
import software.aws.toolkits.jetbrains.services.codewhisperer.service.CodeWhispererInvocationStatus
import software.aws.toolkits.resources.message

class CodeWhispererNavigatePrevAction : AnAction(message("codewhisperer.inline.navigate.previous")), DumbAware {
Expand All @@ -20,14 +19,14 @@ class CodeWhispererNavigatePrevAction : AnAction(message("codewhisperer.inline.n
override fun update(e: AnActionEvent) {
e.presentation.isEnabled = e.project != null &&
e.getData(CommonDataKeys.EDITOR) != null &&
CodeWhispererInvocationStatusNew.getInstance().isDisplaySessionActive()
CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()
}

override fun actionPerformed(e: AnActionEvent) {
val sessionContext = e.project?.getUserData(CodeWhispererServiceNew.KEY_SESSION_CONTEXT) ?: return
if (!CodeWhispererInvocationStatusNew.getInstance().isDisplaySessionActive()) return
val states = e.project?.getUserData(CodeWhispererPopupManager.KEY_INVOCATION_CONTEXT) ?: return
if (!CodeWhispererInvocationStatus.getInstance().isDisplaySessionActive()) return
ApplicationManager.getApplication().messageBus.syncPublisher(
CodeWhispererPopupManager.CODEWHISPERER_USER_ACTION_PERFORMED
).navigatePrevious(sessionContext)
).navigatePrevious(states)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@

package software.aws.toolkits.jetbrains.services.codewhisperer.editor

import com.intellij.notification.NotificationAction
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.command.WriteCommandAction
import com.intellij.openapi.components.Service
import com.intellij.openapi.components.service
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.options.ShowSettingsUtil
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiDocumentManager
import software.aws.toolkits.jetbrains.services.codewhisperer.model.CaretPosition
import software.aws.toolkits.jetbrains.services.codewhisperer.model.InvocationContext
import software.aws.toolkits.jetbrains.services.codewhisperer.model.SessionContext
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager
import software.aws.toolkits.jetbrains.services.codewhisperer.settings.CodeWhispererConfigurable
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererTelemetryService
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CaretMovement
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_BRACKETS
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.PAIRED_QUOTES
import software.aws.toolkits.jetbrains.settings.CodeWhispererSettings
import software.aws.toolkits.jetbrains.utils.notifyInfo
import software.aws.toolkits.resources.message

Check warning

Code scanning / QDJVMC

Usage of redundant or deprecated syntax or deprecated symbols Warning

Remove deprecated symbol import
import java.time.Instant
import java.util.Stack

Expand Down Expand Up @@ -70,6 +76,26 @@
).afterAccept(states, sessionContext, rangeMarker)
}
}

// Display tab accept priority once when the first accept is made
if (!CodeWhispererSettings.getInstance().isTabAcceptPriorityNotificationShownOnce()) {
notifyInfo(
"Amazon Q",
message("codewhisperer.inline.settings.tab_priority.notification.text"),

Check warning

Code scanning / QDJVMC

Usage of redundant or deprecated syntax or deprecated symbols Warning

'message(String, vararg Any): String' is deprecated. Use extension-specific localization bundle instead
project = project,
listOf(
NotificationAction.create(
message("codewhisperer.actions.open_settings.title")

Check warning on line 88 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt

View workflow job for this annotation

GitHub Actions / qodana

Incorrect string capitalization

String 'Open Settings' is not properly capitalized. It should have sentence capitalization

Check warning on line 88 in plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/editor/CodeWhispererEditorManager.kt

View workflow job for this annotation

GitHub Actions / qodana

Usage of redundant or deprecated syntax or deprecated symbols

'message(String, vararg Any): String' is deprecated. Use extension-specific localization bundle instead

Check warning

Code scanning / QDJVMC

Incorrect string capitalization Warning

String 'Open Settings' is not properly capitalized. It should have sentence capitalization

Check warning

Code scanning / QDJVMC

Usage of redundant or deprecated syntax or deprecated symbols Warning

'message(String, vararg Any): String' is deprecated. Use extension-specific localization bundle instead
) { _, notification ->
ShowSettingsUtil.getInstance().showSettingsDialog(project, CodeWhispererConfigurable::class.java)
},
NotificationAction.create(
message("codewhisperer.notification.custom.simple.button.got_it")

Check warning

Code scanning / QDJVMC

Usage of redundant or deprecated syntax or deprecated symbols Warning

'message(String, vararg Any): String' is deprecated. Use extension-specific localization bundle instead
) { _, notification -> notification.expire() }
)
)
CodeWhispererSettings.getInstance().setTabAcceptPriorityNotificationShownOnce(true)
}
}

private fun isMatchingSymbol(symbol: Char): Boolean =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,10 @@ data class SessionContext(
val typeaheadOriginal: String = "",
val selectedIndex: Int = 0,
val seen: MutableSet<Int> = mutableSetOf(),
val isFirstTimeShowingPopup: Boolean = true,
var toBeRemovedHighlighter: RangeHighlighter? = null,
var insertEndOffset: Int = -1,
var isPopupShowing: Boolean = false,
var perceivedLatency: Double = -1.0,
)

data class SessionContextNew(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,29 @@ import javax.swing.JLabel
import javax.swing.JPanel

class CodeWhispererPopupComponents {
val prevButton = createNavigationButton(
message("codewhisperer.popup.button.prev", POPUP_DIM_HEX)
val prevButton = createNavigationButton(prevButtonText())
fun prevButtonText() =
message(
"codewhisperer.popup.button.prev",
POPUP_DIM_HEX,
run {
// TODO: Doesn't reflect dynamically if users change but didn't restart IDE
KeymapUtil.getFirstKeyboardShortcutText(
ActionManager.getInstance().getAction("codewhisperer.inline.navigate.previous")
)
}
)
val nextButton = createNavigationButton(nextButtonText()).apply { preferredSize = prevButton.preferredSize }
fun nextButtonText() = message(
"codewhisperer.popup.button.next",
POPUP_DIM_HEX,
run {
// TODO: Doesn't reflect dynamically if users change but didn't restart IDE
KeymapUtil.getFirstKeyboardShortcutText(
ActionManager.getInstance().getAction("codewhisperer.inline.navigate.next")
)
}
)
val nextButton = createNavigationButton(
message("codewhisperer.popup.button.next", POPUP_DIM_HEX)
).apply {
preferredSize = prevButton.preferredSize
}
val acceptButton = createNavigationButton(
message("codewhisperer.popup.button.accept", POPUP_DIM_HEX)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class CodeWhispererPopupListener(private val states: InvocationContext) : JBPopu
CodeWhispererInvocationStatus.getInstance().popupStartTimestamp?.let { Duration.between(it, Instant.now()) }
)

CodeWhispererInvocationStatus.getInstance().setPopupActive(false)
CodeWhispererInvocationStatus.getInstance().setDisplaySessionActive(false)
}
}

Expand Down
Loading
Loading