feat: make the send shortcut configurable (#1149)

This change makes it so the user can select the key combination to send the message. Users may configure any combination of ctrl, alt, or shift (or the mac equivalents) plus enter. If any are configured, that combination plus enter will send the message, and enter by itself will insert a newline. If none are configured, enter by itself and any of the modifiers plus enter inserts a newline. This means the previous behavior of shift+enter to insert a newline is preserved.
This commit is contained in:
Patrick Hemmer 2025-10-21 07:23:17 -04:00 committed by Carl-Robert Linnupuu
parent 2263afe543
commit dd1a63b6e5
4 changed files with 68 additions and 8 deletions

View file

@ -7,6 +7,7 @@ import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.dsl.builder.panel
import com.intellij.util.ui.JBUI
import ee.carlrobert.codegpt.CodeGPTBundle
import java.awt.Toolkit
class ChatCompletionConfigurationForm {
@ -29,6 +30,21 @@ class ChatCompletionConfigurationForm {
service<ConfigurationSettings>().state.chatCompletionSettings.clickableLinksEnabled
)
private val sendWithAltEnterCheckBox = JBCheckBox(
Toolkit.getProperty("AWT.alt", "Alt"),
service<ConfigurationSettings>().state.chatCompletionSettings.sendWithAltEnter
)
private val sendWithCtrlEnterCheckBox = JBCheckBox(
Toolkit.getProperty("AWT.ctrl", "Ctrl"),
service<ConfigurationSettings>().state.chatCompletionSettings.sendWithCtrlEnter
)
private val sendWithShiftEnterCheckBox = JBCheckBox(
Toolkit.getProperty("AWT.shift", "Shift"),
service<ConfigurationSettings>().state.chatCompletionSettings.sendWithShiftEnter
)
fun createPanel(): DialogPanel {
return panel {
row {
@ -50,6 +66,20 @@ class ChatCompletionConfigurationForm {
cell(psiStructureAnalyzeDepthField)
.comment(CodeGPTBundle.get("configurationConfigurable.section.chatCompletion.psiStructure.analyzeDepth.comment"))
}
group(CodeGPTBundle.get("configurationConfigurable.section.chatCompletion.sendMessageShortcut.title")) {
row {
comment(CodeGPTBundle.get("configurationConfigurable.section.chatCompletion.sendMessageShortcut.description"))
}
row {
cell(sendWithCtrlEnterCheckBox)
}
row {
cell(sendWithAltEnterCheckBox)
}
row {
cell(sendWithShiftEnterCheckBox)
}
}
}.withBorder(JBUI.Borders.emptyLeft(16))
}
@ -58,6 +88,9 @@ class ChatCompletionConfigurationForm {
psiStructureCheckBox.isSelected = prevState.psiStructureEnabled
psiStructureAnalyzeDepthField.number = prevState.psiStructureAnalyzeDepth
clickableLinksCheckBox.isSelected = prevState.clickableLinksEnabled
sendWithAltEnterCheckBox.isSelected = prevState.sendWithAltEnter
sendWithCtrlEnterCheckBox.isSelected = prevState.sendWithCtrlEnter
sendWithShiftEnterCheckBox.isSelected = prevState.sendWithShiftEnter
}
fun getFormState(): ChatCompletionSettingsState {
@ -66,6 +99,9 @@ class ChatCompletionConfigurationForm {
this.psiStructureEnabled = psiStructureCheckBox.isSelected
this.psiStructureAnalyzeDepth = psiStructureAnalyzeDepthField.number
this.clickableLinksEnabled = clickableLinksCheckBox.isSelected
this.sendWithAltEnter = sendWithAltEnterCheckBox.isSelected
this.sendWithCtrlEnter = sendWithCtrlEnterCheckBox.isSelected
this.sendWithShiftEnter = sendWithShiftEnterCheckBox.isSelected
}
}
}

View file

@ -50,6 +50,9 @@ class ChatCompletionSettingsState : BaseState() {
var psiStructureEnabled by property(true)
var psiStructureAnalyzeDepth by property(3)
var clickableLinksEnabled by property(true)
var sendWithAltEnter by property(false)
var sendWithCtrlEnter by property(false)
var sendWithShiftEnter by property(false)
}
class CodeCompletionSettingsState : BaseState() {

View file

@ -1,6 +1,5 @@
package ee.carlrobert.codegpt.ui.textarea
import com.intellij.codeInsight.lookup.impl.LookupImpl
import com.intellij.ide.IdeEventQueue
import com.intellij.openapi.application.runUndoTransparentWriteAction
import com.intellij.openapi.util.TextRange
@ -13,6 +12,7 @@ import java.awt.event.KeyEvent
import java.awt.event.MouseEvent
import java.util.*
import com.intellij.openapi.ide.CopyPasteManager
import ee.carlrobert.codegpt.settings.configuration.ConfigurationSettings
import java.awt.datatransfer.DataFlavor
class PromptTextFieldEventDispatcher(
@ -42,12 +42,31 @@ class PromptTextFieldEventDispatcher(
}
}
KeyEvent.VK_ENTER -> {
if (e.isShiftDown) {
handleShiftEnter(e)
} else if (e.modifiersEx and InputEvent.ALT_DOWN_MASK == 0
&& e.modifiersEx and InputEvent.CTRL_DOWN_MASK == 0
) {
onSubmit(e)
val settings =
ConfigurationSettings.getState().chatCompletionSettings
val anyModifierConfigured =
settings.sendWithCtrlEnter || settings.sendWithAltEnter || settings.sendWithShiftEnter
if (anyModifierConfigured) {
var expectedModifiers = 0
if (settings.sendWithCtrlEnter) expectedModifiers = expectedModifiers or InputEvent.CTRL_DOWN_MASK
if (settings.sendWithAltEnter) expectedModifiers = expectedModifiers or InputEvent.ALT_DOWN_MASK
if (settings.sendWithShiftEnter) expectedModifiers = expectedModifiers or InputEvent.SHIFT_DOWN_MASK
val eventModifiers = e.modifiersEx and (InputEvent.CTRL_DOWN_MASK or InputEvent.ALT_DOWN_MASK or InputEvent.SHIFT_DOWN_MASK)
if (eventModifiers == expectedModifiers) {
onSubmit(e)
} else {
handleNewline(e)
}
} else {
// No modifiers configured: send on plain Enter
if (!e.isControlDown && !e.isAltDown && !e.isShiftDown) {
onSubmit(e)
} else {
handleNewline(e)
}
}
}
}
@ -65,7 +84,7 @@ class PromptTextFieldEventDispatcher(
}
}
private fun handleShiftEnter(e: KeyEvent) {
private fun handleNewline(e: KeyEvent) {
val parent = findParent()
if (parent is PromptTextField) {
runUndoTransparentWriteAction {

View file

@ -168,6 +168,8 @@ configurationConfigurable.section.chatCompletion.psiStructure.analyzeDepth.comme
configurationConfigurable.section.chatCompletion.psiStructure.description=If enabled, the class structure that is present in the imports of the attached files will be added in the context of the dialog. A structure refers to the source code in files that include constructors, fields, and methods, with all modifiers, arguments, and return types, but without an implementation. The implementation of dependencies is intentionally excluded in order to find a balance between a high-quality chat context and saving tokens.
configurationConfigurable.section.chatCompletion.clickableLinks.title=Show clickable links for classes and methods
configurationConfigurable.section.chatCompletion.clickableLinks.description=If enabled, code references in answers become clickable so you can jump to them in your IDE.
configurationConfigurable.section.chatCompletion.sendMessageShortcut.title=Send Message Shortcut
configurationConfigurable.section.chatCompletion.sendMessageShortcut.description=If none are selected, 'Enter' by itself sends the message. If any are selected, the chosen shortcut plus 'Enter' sends the message and 'Enter' by itself inserts a newline.
settingsConfigurable.service.llama.predefinedModel.comment=Download and use vetted models from HuggingFace.
settingsConfigurable.service.llama.customModel.comment=Use your own GGUF model file from a local path on your computer.
settingsConfigurable.service.custom.openai.testConnection.label=Test Connection