diff --git a/CHANGELOG.md b/CHANGELOG.md
index f0ca9885..5ddda3b4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,7 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
-- 'Direct Apply' and 'Compare with Original' tool window editor actions for generated code
+- Multiple tool window editor actions for generated code:
+ - Direct apply: Directly replaces the highlighted code in the main editor with the generated code
+ - Compare with Original: Opens a diff view to compare the generated code with the highlighted code
+ - Insert at Caret: Inserts the generated code at the exact location of the caret in the main editor
- Vision support for Azure models
- General improvements to code completions, including:
- Proper streaming support
diff --git a/src/main/java/ee/carlrobert/codegpt/Icons.java b/src/main/java/ee/carlrobert/codegpt/Icons.java
index bc618476..fb680fac 100644
--- a/src/main/java/ee/carlrobert/codegpt/Icons.java
+++ b/src/main/java/ee/carlrobert/codegpt/Icons.java
@@ -28,5 +28,7 @@ public final class Icons {
public static final Icon Upload = IconLoader.getIcon("/icons/upload.svg", Icons.class);
public static final Icon GreenCheckmark =
IconLoader.getIcon("/icons/greenCheckmark.svg", Icons.class);
+ public static final Icon SendToTheLeft =
+ IconLoader.getIcon("/icons/sendToTheLeft.svg", Icons.class);
public static final Icon StatusBarCompletionInProgress = new AnimatedIcon.Default();
}
diff --git a/src/main/java/ee/carlrobert/codegpt/actions/ActionType.java b/src/main/java/ee/carlrobert/codegpt/actions/ActionType.java
index 0ba9b313..f8ab4a56 100644
--- a/src/main/java/ee/carlrobert/codegpt/actions/ActionType.java
+++ b/src/main/java/ee/carlrobert/codegpt/actions/ActionType.java
@@ -13,6 +13,7 @@ public enum ActionType {
CREATE_NEW_FILE,
COPY_CODE,
REPLACE_IN_MAIN_EDITOR,
+ INSERT_AT_CARET,
RELOAD_MESSAGE,
CHANGE_PROVIDER
}
diff --git a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/ReplaceCodeInMainEditorAction.java b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/ReplaceCodeInMainEditorAction.java
index 6c6b7402..1ba9aacd 100644
--- a/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/ReplaceCodeInMainEditorAction.java
+++ b/src/main/java/ee/carlrobert/codegpt/actions/toolwindow/ReplaceCodeInMainEditorAction.java
@@ -14,7 +14,7 @@
public class ReplaceCodeInMainEditorAction extends AnAction {
public ReplaceCodeInMainEditorAction() {
- super("Replace in Main Editor", "Replace code in main editor", AllIcons.Actions.Replace);
+ super("Replace Selection", "Replace selected code in main editor", AllIcons.Actions.Replace);
EditorActionsUtil.registerAction(this);
}
diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.java
index d55bc2c4..f259ddf0 100644
--- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.java
+++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/ResponseEditorPanel.java
@@ -33,6 +33,7 @@
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.CopyAction;
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.DiffAction;
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.EditAction;
+import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.InsertAtCaretAction;
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.NewFileAction;
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.ReplaceSelectionAction;
import ee.carlrobert.codegpt.ui.IconActionButton;
@@ -82,9 +83,9 @@ public ResponseEditorPanel(
if (highlightedText != null && !highlightedText.isEmpty()) {
directLinksPanel.setVisible(false);
- directLinksPanel.setBorder(JBUI.Borders.emptyTop(4));
+ directLinksPanel.setBorder(JBUI.Borders.emptyTop(8));
directLinksPanel.add(new CompareWithOriginalActionLink(project, editor, highlightedText));
- directLinksPanel.add(Box.createHorizontalStrut(8));
+ directLinksPanel.add(Box.createHorizontalStrut(12));
directLinksPanel.add(new DirectApplyActionLink(project, editor, highlightedText));
add(directLinksPanel, BorderLayout.SOUTH);
}
@@ -179,6 +180,7 @@ private ActionToolbar createHeaderActions(String extension, EditorEx editorEx) {
var actionGroup = new DefaultCompactActionGroup("EDITOR_TOOLBAR_ACTION_GROUP", false);
actionGroup.add(new CopyAction(editor));
actionGroup.add(new ReplaceSelectionAction(editor));
+ actionGroup.add(new InsertAtCaretAction(editor));
actionGroup.addSeparator();
var wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/DiffAction.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/DiffAction.java
index 373787a4..b1ea4d4c 100644
--- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/DiffAction.java
+++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/DiffAction.java
@@ -15,18 +15,18 @@
public class DiffAction extends AbstractAction {
- private final EditorEx toolwindowEditor;
+ private final EditorEx editor;
private final Point locationOnScreen;
- public DiffAction(EditorEx toolwindowEditor, @Nullable Point locationOnScreen) {
+ public DiffAction(EditorEx editor, @Nullable Point locationOnScreen) {
super("Diff", Actions.DiffWithClipboard);
- this.toolwindowEditor = toolwindowEditor;
+ this.editor = editor;
this.locationOnScreen = locationOnScreen;
}
@Override
public void actionPerformed(ActionEvent event) {
- var project = requireNonNull(toolwindowEditor.getProject());
+ var project = requireNonNull(editor.getProject());
var mainEditor = FileEditorManager.getInstance(project).getSelectedTextEditor();
if (mainEditor != null && !EditorUtil.hasSelection(mainEditor) && locationOnScreen != null) {
OverlayUtil.showSelectedEditorSelectionWarning(project, locationOnScreen);
@@ -35,7 +35,7 @@ public void actionPerformed(ActionEvent event) {
EditorDiffUtil.showDiff(
project,
- toolwindowEditor,
+ editor,
mainEditor.getSelectionModel().getSelectedText());
}
}
diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/InsertAtCaretAction.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/InsertAtCaretAction.java
new file mode 100644
index 00000000..83ead8fe
--- /dev/null
+++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/editor/actions/InsertAtCaretAction.java
@@ -0,0 +1,70 @@
+package ee.carlrobert.codegpt.toolwindow.chat.editor.actions;
+
+import static com.intellij.openapi.application.ActionsKt.runUndoTransparentWriteAction;
+
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import ee.carlrobert.codegpt.CodeGPTBundle;
+import ee.carlrobert.codegpt.Icons;
+import ee.carlrobert.codegpt.actions.ActionType;
+import ee.carlrobert.codegpt.actions.TrackableAction;
+import ee.carlrobert.codegpt.ui.OverlayUtil;
+import java.awt.Point;
+import java.util.Optional;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class InsertAtCaretAction extends TrackableAction {
+
+ public InsertAtCaretAction(@NotNull Editor editor) {
+ super(
+ editor,
+ CodeGPTBundle.get("toolwindow.chat.editor.action.insertAtCaret.title"),
+ CodeGPTBundle.get("toolwindow.chat.editor.action.insertAtCaret.description"),
+ Icons.SendToTheLeft,
+ ActionType.INSERT_AT_CARET);
+ }
+
+ @Override
+ public void handleAction(@NotNull AnActionEvent event) {
+ Point locationOnScreen = getLocationOnScreen(event);
+ Editor mainEditor = getSelectedTextEditor();
+
+ if (mainEditor == null) {
+ OverlayUtil.showWarningBalloon("Active editor not found", locationOnScreen);
+ return;
+ }
+
+ insertTextAtCaret(mainEditor, locationOnScreen);
+ }
+
+ @Nullable
+ private Point getLocationOnScreen(AnActionEvent event) {
+ return Optional.ofNullable(event.getInputEvent())
+ .map(inputEvent -> inputEvent.getComponent().getLocationOnScreen())
+ .orElse(null);
+ }
+
+ @Nullable
+ private Editor getSelectedTextEditor() {
+ return Optional.ofNullable(editor.getProject())
+ .map(FileEditorManager::getInstance)
+ .map(FileEditorManager::getSelectedTextEditor)
+ .orElse(null);
+ }
+
+ private void insertTextAtCaret(Editor mainEditor, @Nullable Point locationOnScreen) {
+ runUndoTransparentWriteAction(() -> {
+ mainEditor.getDocument().insertString(
+ mainEditor.getCaretModel().getOffset(),
+ editor.getDocument().getText());
+ if (locationOnScreen != null) {
+ OverlayUtil.showInfoBalloon(
+ "Text successfully inserted at the current cursor position.",
+ locationOnScreen);
+ }
+ return null;
+ });
+ }
+}
diff --git a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/CompareWithOriginalActionLink.kt b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/CompareWithOriginalActionLink.kt
index 317074ef..6927ff10 100644
--- a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/CompareWithOriginalActionLink.kt
+++ b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/CompareWithOriginalActionLink.kt
@@ -1,5 +1,6 @@
package ee.carlrobert.codegpt.toolwindow.chat
+import com.intellij.icons.AllIcons.Actions
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.editor.Editor
@@ -18,6 +19,10 @@ class CompareWithOriginalActionLink(
highlightedText
) {
+ init {
+ setIcon(Actions.Diff)
+ }
+
class CompareWithOriginalAction(
private val project: Project,
private val toolwindowEditor: Editor,
diff --git a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/DirectApplyActionLink.kt b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/DirectApplyActionLink.kt
index 27d130ac..09a6863f 100644
--- a/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/DirectApplyActionLink.kt
+++ b/src/main/kotlin/ee/carlrobert/codegpt/toolwindow/chat/DirectApplyActionLink.kt
@@ -1,5 +1,6 @@
package ee.carlrobert.codegpt.toolwindow.chat
+import com.intellij.icons.AllIcons.Actions
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.command.WriteCommandAction
@@ -20,6 +21,10 @@ class DirectApplyActionLink(
highlightedText
) {
+ init {
+ setIcon(Actions.Selectall)
+ }
+
class DirectApplyAction(
private val project: Project,
private val toolwindowEditor: Editor,
diff --git a/src/main/resources/icons/sendToTheLeft.svg b/src/main/resources/icons/sendToTheLeft.svg
new file mode 100644
index 00000000..b130d9ee
--- /dev/null
+++ b/src/main/resources/icons/sendToTheLeft.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/src/main/resources/icons/sendToTheLeft_dark.svg b/src/main/resources/icons/sendToTheLeft_dark.svg
new file mode 100644
index 00000000..5cd95941
--- /dev/null
+++ b/src/main/resources/icons/sendToTheLeft_dark.svg
@@ -0,0 +1,5 @@
+
+
diff --git a/src/main/resources/messages/codegpt.properties b/src/main/resources/messages/codegpt.properties
index a576ac98..c1892cdb 100644
--- a/src/main/resources/messages/codegpt.properties
+++ b/src/main/resources/messages/codegpt.properties
@@ -181,6 +181,8 @@ toolwindow.chat.editor.action.newFile.title=New File
toolwindow.chat.editor.action.newFile.description=Create new file from generated code
toolwindow.chat.editor.action.replaceSelection.title=Replace Selection
toolwindow.chat.editor.action.replaceSelection.description=Replace main editor selected code
+toolwindow.chat.editor.action.insertAtCaret.title=Insert at Caret
+toolwindow.chat.editor.action.insertAtCaret.description=Insert generated code after main editor caret position
toolwindow.chat.editor.action.expand=Show More (+%s rows)
toolwindow.chat.editor.action.collapse=Show Less
toolwindow.chat.response.action.reloadResponse.text=Reload Response