mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-20 09:24:08 +00:00
feat: auto apply (#743)
This commit is contained in:
parent
ffa1fbbacf
commit
6fbea7d4b8
25 changed files with 301 additions and 232 deletions
|
|
@ -28,6 +28,9 @@ public final class Icons {
|
|||
public static final Icon Ollama = IconLoader.getIcon("/icons/ollama.svg", Icons.class);
|
||||
public static final Icon User = IconLoader.getIcon("/icons/user.svg", Icons.class);
|
||||
public static final Icon Upload = IconLoader.getIcon("/icons/upload.svg", Icons.class);
|
||||
public static final Icon Lightning = IconLoader.getIcon("/icons/lightning.svg", Icons.class);
|
||||
public static final Icon LightningDisabled =
|
||||
IconLoader.getIcon("/icons/lightning.svg", Icons.class);
|
||||
public static final Icon GreenCheckmark =
|
||||
IconLoader.getIcon("/icons/greenCheckmark.svg", Icons.class);
|
||||
public static final Icon SendToTheLeft =
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ public enum ActionType {
|
|||
EDIT_CODE,
|
||||
CREATE_NEW_FILE,
|
||||
COPY_CODE,
|
||||
AUTO_APPLY,
|
||||
REPLACE_IN_MAIN_EDITOR,
|
||||
INSERT_AT_CARET,
|
||||
RELOAD_MESSAGE,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package ee.carlrobert.codegpt.actions;
|
|||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
|
||||
import javax.swing.Icon;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
|
@ -10,16 +9,13 @@ import org.jetbrains.annotations.NotNull;
|
|||
public abstract class TrackableAction extends AnAction {
|
||||
|
||||
private final ActionType actionType;
|
||||
protected final Editor editor;
|
||||
|
||||
public TrackableAction(
|
||||
@NotNull Editor editor,
|
||||
String text,
|
||||
String description,
|
||||
Icon icon,
|
||||
ActionType actionType) {
|
||||
super(text, description, icon);
|
||||
this.editor = editor;
|
||||
this.actionType = actionType;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -116,7 +116,6 @@ abstract class ToolWindowCompletionResponseEventListener implements
|
|||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
try {
|
||||
responsePanel.enableActions();
|
||||
responseContainer.enableActions();
|
||||
if (!responseContainer.isResponseReceived() && !fullMessage.isEmpty()) {
|
||||
responseContainer.withResponse(fullMessage);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import com.intellij.openapi.actionSystem.ActionToolbar;
|
|||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.actionSystem.DefaultCompactActionGroup;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
|
|
@ -28,19 +27,15 @@ import com.intellij.ui.components.ActionLink;
|
|||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.actions.toolwindow.ReplaceCodeInMainEditorAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.CompareWithOriginalActionLink;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.DirectApplyActionLink;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.AutoApplyAction;
|
||||
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;
|
||||
import ee.carlrobert.codegpt.util.EditorUtil;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
|
@ -48,7 +43,6 @@ import org.jetbrains.annotations.Nullable;
|
|||
public class ResponseEditorPanel extends JPanel implements Disposable {
|
||||
|
||||
private final Editor editor;
|
||||
private final JPanel directLinksPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, 0, 0));
|
||||
|
||||
public ResponseEditorPanel(
|
||||
Project project,
|
||||
|
|
@ -75,28 +69,16 @@ public class ResponseEditorPanel extends JPanel implements Disposable {
|
|||
}
|
||||
}
|
||||
configureEditor(
|
||||
project,
|
||||
(EditorEx) editor,
|
||||
readOnly,
|
||||
new ContextMenuPopupHandler.Simple(group),
|
||||
findLanguageExtensionMapping(markdownLanguage).getValue());
|
||||
add(editor.getComponent(), BorderLayout.CENTER);
|
||||
|
||||
if (highlightedText != null && !highlightedText.isEmpty()) {
|
||||
directLinksPanel.setVisible(false);
|
||||
directLinksPanel.setBorder(JBUI.Borders.emptyTop(8));
|
||||
directLinksPanel.add(new CompareWithOriginalActionLink(project, editor, highlightedText));
|
||||
directLinksPanel.add(Box.createHorizontalStrut(12));
|
||||
directLinksPanel.add(new DirectApplyActionLink(project, editor, highlightedText));
|
||||
add(directLinksPanel, BorderLayout.SOUTH);
|
||||
}
|
||||
|
||||
Disposer.register(disposableParent, this);
|
||||
}
|
||||
|
||||
public void showEditorActions() {
|
||||
directLinksPanel.setVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
EditorFactory.getInstance().releaseEditor(editor);
|
||||
|
|
@ -107,6 +89,7 @@ public class ResponseEditorPanel extends JPanel implements Disposable {
|
|||
}
|
||||
|
||||
private void configureEditor(
|
||||
Project project,
|
||||
EditorEx editorEx,
|
||||
boolean readOnly,
|
||||
ContextMenuPopupHandler popupHandler,
|
||||
|
|
@ -133,22 +116,28 @@ public class ResponseEditorPanel extends JPanel implements Disposable {
|
|||
editorEx.setVerticalScrollbarVisible(false);
|
||||
editorEx.getContentComponent().setBorder(JBUI.Borders.emptyLeft(4));
|
||||
editorEx.setBorder(IdeBorderFactory.createBorder(ColorUtil.fromHex("#48494b")));
|
||||
editorEx.setPermanentHeaderComponent(createHeaderComponent(editorEx, extension, readOnly));
|
||||
editorEx.setPermanentHeaderComponent(
|
||||
createHeaderComponent(project, editorEx, extension, readOnly));
|
||||
editorEx.setHeaderComponent(null);
|
||||
}
|
||||
|
||||
private JPanel createHeaderComponent(EditorEx editorEx, String extension, boolean readOnly) {
|
||||
var headerComponent = new JPanel(new BorderLayout());
|
||||
headerComponent.setBorder(
|
||||
private JPanel createHeaderComponent(
|
||||
Project project,
|
||||
EditorEx editorEx,
|
||||
String extension,
|
||||
boolean readOnly) {
|
||||
var headerPanel = new JPanel(new BorderLayout());
|
||||
headerPanel.setBorder(
|
||||
JBUI.Borders.compound(
|
||||
JBUI.Borders.customLine(ColorUtil.fromHex("#48494b"), 1, 1, 0, 1),
|
||||
JBUI.Borders.empty(4)));
|
||||
headerComponent.add(createExpandLink(editorEx), BorderLayout.LINE_START);
|
||||
headerPanel.add(createExpandLink(editorEx), BorderLayout.LINE_START);
|
||||
if (!readOnly) {
|
||||
headerComponent.add(
|
||||
createHeaderActions(extension, editorEx).getComponent(), BorderLayout.LINE_END);
|
||||
headerPanel.add(
|
||||
createHeaderActions(project, extension, editorEx, headerPanel).getComponent(),
|
||||
BorderLayout.LINE_END);
|
||||
}
|
||||
return headerComponent;
|
||||
return headerPanel;
|
||||
}
|
||||
|
||||
private String getLinkText(boolean expanded) {
|
||||
|
|
@ -178,20 +167,20 @@ public class ResponseEditorPanel extends JPanel implements Disposable {
|
|||
return expandLink;
|
||||
}
|
||||
|
||||
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));
|
||||
private ActionToolbar createHeaderActions(
|
||||
Project project,
|
||||
String extension,
|
||||
EditorEx editorEx,
|
||||
JPanel headerPanel) {
|
||||
var actionGroup = new DefaultActionGroup("EDITOR_TOOLBAR_ACTION_GROUP", false);
|
||||
actionGroup.add(new AutoApplyAction(project, editorEx, headerPanel));
|
||||
actionGroup.add(new InsertAtCaretAction(editorEx));
|
||||
actionGroup.add(new CopyAction(editorEx));
|
||||
actionGroup.addSeparator();
|
||||
|
||||
var wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
wrapper.add(new IconActionButton(new CopyAction(editor)));
|
||||
wrapper.add(Box.createHorizontalStrut(4));
|
||||
wrapper.add(new IconActionButton(new ReplaceSelectionAction(editor)));
|
||||
|
||||
var menu = new JBPopupMenu();
|
||||
menu.add(new JBMenuItem(new DiffAction(editorEx, menu.getLocation())));
|
||||
menu.add(new JBMenuItem(new ReplaceSelectionAction(editorEx, menu.getLocation())));
|
||||
menu.add(new JBMenuItem(new EditAction(editorEx)));
|
||||
menu.add(new JBMenuItem(new NewFileAction(editorEx, extension)));
|
||||
|
||||
|
|
|
|||
|
|
@ -15,18 +15,20 @@ import org.jetbrains.annotations.NotNull;
|
|||
|
||||
public class CopyAction extends TrackableAction {
|
||||
|
||||
public CopyAction(@NotNull Editor editor) {
|
||||
private final @NotNull Editor toolwindowEditor;
|
||||
|
||||
public CopyAction(@NotNull Editor toolwindowEditor) {
|
||||
super(
|
||||
editor,
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.copy.title"),
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.copy.description"),
|
||||
Actions.Copy,
|
||||
ActionType.COPY_CODE);
|
||||
this.toolwindowEditor = toolwindowEditor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAction(@NotNull AnActionEvent event) {
|
||||
StringSelection stringSelection = new StringSelection(editor.getDocument().getText());
|
||||
StringSelection stringSelection = new StringSelection(toolwindowEditor.getDocument().getText());
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
clipboard.setContents(stringSelection, null);
|
||||
|
||||
|
|
@ -36,8 +38,8 @@ public class CopyAction extends TrackableAction {
|
|||
locationOnScreen.y = locationOnScreen.y - 16;
|
||||
|
||||
OverlayUtil.showInfoBalloon(
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.copy.success"),
|
||||
locationOnScreen);
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.copy.success"),
|
||||
locationOnScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public class DiffAction extends AbstractAction {
|
|||
private final Point locationOnScreen;
|
||||
|
||||
public DiffAction(EditorEx editor, @Nullable Point locationOnScreen) {
|
||||
super("Diff", Actions.DiffWithClipboard);
|
||||
super("Diff Selection", Actions.DiffWithClipboard);
|
||||
this.editor = editor;
|
||||
this.locationOnScreen = locationOnScreen;
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ public class DiffAction extends AbstractAction {
|
|||
public void actionPerformed(ActionEvent event) {
|
||||
var project = requireNonNull(editor.getProject());
|
||||
var mainEditor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (mainEditor != null && !EditorUtil.hasSelection(mainEditor) && locationOnScreen != null) {
|
||||
if (mainEditor == null || !EditorUtil.hasSelection(mainEditor)) {
|
||||
OverlayUtil.showSelectedEditorSelectionWarning(project, locationOnScreen);
|
||||
return;
|
||||
}
|
||||
|
|
@ -36,6 +36,6 @@ public class DiffAction extends AbstractAction {
|
|||
EditorDiffUtil.showDiff(
|
||||
project,
|
||||
editor,
|
||||
mainEditor.getSelectionModel().getSelectedText());
|
||||
requireNonNull(mainEditor.getSelectionModel().getSelectedText()));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,13 +17,15 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
public class InsertAtCaretAction extends TrackableAction {
|
||||
|
||||
public InsertAtCaretAction(@NotNull Editor editor) {
|
||||
private final @NotNull Editor toolwindowEditor;
|
||||
|
||||
public InsertAtCaretAction(@NotNull Editor toolwindowEditor) {
|
||||
super(
|
||||
editor,
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.insertAtCaret.title"),
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.insertAtCaret.description"),
|
||||
Icons.SendToTheLeft,
|
||||
ActionType.INSERT_AT_CARET);
|
||||
this.toolwindowEditor = toolwindowEditor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -48,7 +50,7 @@ public class InsertAtCaretAction extends TrackableAction {
|
|||
|
||||
@Nullable
|
||||
private Editor getSelectedTextEditor() {
|
||||
return Optional.ofNullable(editor.getProject())
|
||||
return Optional.ofNullable(toolwindowEditor.getProject())
|
||||
.map(FileEditorManager::getInstance)
|
||||
.map(FileEditorManager::getSelectedTextEditor)
|
||||
.orElse(null);
|
||||
|
|
@ -58,7 +60,7 @@ public class InsertAtCaretAction extends TrackableAction {
|
|||
runUndoTransparentWriteAction(() -> {
|
||||
mainEditor.getDocument().insertString(
|
||||
mainEditor.getCaretModel().getOffset(),
|
||||
editor.getDocument().getText());
|
||||
toolwindowEditor.getDocument().getText());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,36 +3,41 @@ package ee.carlrobert.codegpt.toolwindow.chat.editor.actions;
|
|||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.intellij.icons.AllIcons.Actions;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.actions.ActionType;
|
||||
import ee.carlrobert.codegpt.actions.TrackableAction;
|
||||
import ee.carlrobert.codegpt.ui.OverlayUtil;
|
||||
import ee.carlrobert.codegpt.util.EditorUtil;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.ActionEvent;
|
||||
import javax.swing.AbstractAction;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ReplaceSelectionAction extends TrackableAction {
|
||||
public class ReplaceSelectionAction extends AbstractAction {
|
||||
|
||||
public ReplaceSelectionAction(@NotNull Editor editor) {
|
||||
private final @NotNull EditorEx toolwindowEditor;
|
||||
private final Point locationOnScreen;
|
||||
|
||||
public ReplaceSelectionAction(
|
||||
@NotNull EditorEx toolwindowEditor,
|
||||
@Nullable Point locationOnScreen) {
|
||||
super(
|
||||
editor,
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.replaceSelection.title"),
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.replaceSelection.description"),
|
||||
Actions.Replace,
|
||||
ActionType.REPLACE_IN_MAIN_EDITOR);
|
||||
Actions.Replace);
|
||||
this.toolwindowEditor = toolwindowEditor;
|
||||
this.locationOnScreen = locationOnScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAction(@NotNull AnActionEvent event) {
|
||||
var project = requireNonNull(event.getProject());
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
var project = requireNonNull(toolwindowEditor.getProject());
|
||||
if (EditorUtil.isMainEditorTextSelected(project)) {
|
||||
var mainEditor = EditorUtil.getSelectedEditor(project);
|
||||
if (mainEditor != null) {
|
||||
EditorUtil.replaceEditorSelection(mainEditor, editor.getDocument().getText());
|
||||
EditorUtil.replaceEditorSelection(mainEditor, toolwindowEditor.getDocument().getText());
|
||||
}
|
||||
} else {
|
||||
OverlayUtil.showSelectedEditorSelectionWarning(event);
|
||||
OverlayUtil.showSelectedEditorSelectionWarning(project, locationOnScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,14 +98,6 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
public void enableActions() {
|
||||
if (highlightedText != null
|
||||
&& !highlightedText.isEmpty()
|
||||
&& currentlyProcessedEditorPanel != null) {
|
||||
currentlyProcessedEditorPanel.showEditorActions();
|
||||
}
|
||||
}
|
||||
|
||||
public ChatMessageResponseBody withResponse(String response) {
|
||||
try {
|
||||
for (var message : MarkdownUtil.splitCodeBlocks(response)) {
|
||||
|
|
@ -173,7 +165,7 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
"<html><p style=\"margin-top: 4px; margin-bottom: 8px;\">%s</p></html>",
|
||||
message);
|
||||
if (responseReceived) {
|
||||
add(createTextPane(errorText, false));
|
||||
add(createTextPane(errorText));
|
||||
} else {
|
||||
currentlyProcessedTextPane.setText(errorText);
|
||||
}
|
||||
|
|
@ -269,12 +261,6 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
}
|
||||
|
||||
private void prepareProcessingText(boolean caretVisible) {
|
||||
if (highlightedText != null
|
||||
&& !highlightedText.isEmpty()
|
||||
&& currentlyProcessedEditorPanel != null) {
|
||||
currentlyProcessedEditorPanel.showEditorActions();
|
||||
}
|
||||
|
||||
currentlyProcessedEditorPanel = null;
|
||||
currentlyProcessedTextPane = createTextPane("", caretVisible);
|
||||
add(currentlyProcessedTextPane);
|
||||
|
|
@ -316,6 +302,10 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
private JTextPane createTextPane(String text) {
|
||||
return createTextPane(text, false);
|
||||
}
|
||||
|
||||
private JTextPane createTextPane(String text, boolean caretVisible) {
|
||||
var textPane = UIUtil.createTextPane(text, false, event -> {
|
||||
if (FileUtil.exists(event.getDescription()) && ACTIVATED.equals(event.getEventType())) {
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ public class ResponsePanel extends JPanel {
|
|||
|
||||
Body() {
|
||||
super(new BorderLayout());
|
||||
setBorder(JBUI.Borders.empty(4, 8, 8, 8));
|
||||
setBorder(JBUI.Borders.empty(4, 8));
|
||||
}
|
||||
|
||||
public void addContent(JComponent content) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue