feat: new 'Insert at Caret' toolwindow editor action

This commit is contained in:
Carl-Robert Linnupuu 2024-09-05 01:21:28 +03:00
parent 4b80fa2a9d
commit baa99d97d1
12 changed files with 109 additions and 9 deletions

View file

@ -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

View file

@ -27,4 +27,6 @@ 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);
}

View file

@ -13,6 +13,7 @@ public enum ActionType {
CREATE_NEW_FILE,
COPY_CODE,
REPLACE_IN_MAIN_EDITOR,
INSERT_AT_CARET,
RELOAD_MESSAGE,
CHANGE_PROVIDER
}

View file

@ -14,7 +14,7 @@ import org.jetbrains.annotations.NotNull;
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);
}

View file

@ -33,6 +33,7 @@ import ee.carlrobert.codegpt.toolwindow.chat.DirectApplyActionLink;
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 class ResponseEditorPanel extends JPanel implements Disposable {
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 @@ public class ResponseEditorPanel extends JPanel implements Disposable {
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));

View file

@ -15,18 +15,18 @@ import org.jetbrains.annotations.Nullable;
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 class DiffAction extends AbstractAction {
EditorDiffUtil.showDiff(
project,
toolwindowEditor,
editor,
mainEditor.getSelectionModel().getSelectedText());
}
}

View file

@ -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;
});
}
}

View file

@ -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,

View file

@ -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,

View file

@ -0,0 +1,5 @@
<!-- Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.85355 3.14742C9.04882 3.34269 9.04882 3.65927 8.85355 3.85453L5.70735 7.00074L13.5009 7.00074C13.777 7.00074 14.0009 7.2246 14.0009 7.50074C14.0009 7.77688 13.777 8.00074 13.5009 8.00074L5.70687 8.00074L8.85356 11.1474C9.04882 11.3427 9.04882 11.6593 8.85356 11.8545C8.65829 12.0498 8.34171 12.0498 8.14645 11.8545L4.14645 7.85453C4.05268 7.76076 4 7.63359 4 7.50098C4 7.36837 4.05268 7.24119 4.14645 7.14742L8.14645 3.14742C8.34171 2.95216 8.65829 2.95216 8.85355 3.14742Z" fill="#3574F0"/>
<path d="M2.5 11.5V3.5" stroke="#6C707E" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 824 B

View file

@ -0,0 +1,5 @@
<!-- Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. -->
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M8.85355 3.14742C9.04882 3.34269 9.04882 3.65927 8.85355 3.85453L5.70735 7.00074L13.5009 7.00074C13.777 7.00074 14.0009 7.2246 14.0009 7.50074C14.0009 7.77688 13.777 8.00074 13.5009 8.00074L5.70687 8.00074L8.85356 11.1474C9.04882 11.3427 9.04882 11.6593 8.85356 11.8545C8.65829 12.0498 8.34171 12.0498 8.14645 11.8545L4.14645 7.85453C4.05268 7.76076 4 7.63359 4 7.50098C4 7.36837 4.05268 7.24119 4.14645 7.14742L8.14645 3.14742C8.34171 2.95216 8.65829 2.95216 8.85355 3.14742Z" fill="#548AF7"/>
<path d="M2.5 11.5V3.5" stroke="#CED0D6" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 824 B

View file

@ -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