mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-27 08:53:52 +00:00
feat: add apply and diff actions for toolwindow code editor
This commit is contained in:
parent
43a081dde7
commit
9d21057738
15 changed files with 303 additions and 59 deletions
|
|
@ -10,21 +10,24 @@ public class CallParameters {
|
|||
private final ConversationType conversationType;
|
||||
private final Message message;
|
||||
private final boolean retry;
|
||||
private final String highlightedText;
|
||||
private @Nullable String imageMediaType;
|
||||
private byte[] imageData;
|
||||
|
||||
public CallParameters(Conversation conversation, Message message) {
|
||||
this(conversation, ConversationType.DEFAULT, message, false);
|
||||
this(conversation, ConversationType.DEFAULT, message, null, false);
|
||||
}
|
||||
|
||||
public CallParameters(
|
||||
Conversation conversation,
|
||||
ConversationType conversationType,
|
||||
Message message,
|
||||
@Nullable String highlightedText,
|
||||
boolean retry) {
|
||||
this.conversation = conversation;
|
||||
this.conversationType = conversationType;
|
||||
this.message = message;
|
||||
this.highlightedText = highlightedText;
|
||||
this.retry = retry;
|
||||
}
|
||||
|
||||
|
|
@ -59,4 +62,8 @@ public class CallParameters {
|
|||
public void setImageData(byte[] imageData) {
|
||||
this.imageData = imageData;
|
||||
}
|
||||
|
||||
public @Nullable String getHighlightedText() {
|
||||
return highlightedText;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -118,6 +118,13 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
}
|
||||
|
||||
public void sendMessage(Message message, ConversationType conversationType) {
|
||||
sendMessage(message, conversationType, null);
|
||||
}
|
||||
|
||||
public void sendMessage(
|
||||
Message message,
|
||||
ConversationType conversationType,
|
||||
@Nullable String highlightedText) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
var referencedFiles = project.getUserData(CodeGPTKeys.SELECTED_FILES);
|
||||
var chatToolWindowPanel = project.getService(ChatToolWindowContentManager.class)
|
||||
|
|
@ -138,7 +145,8 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
|
||||
var userMessagePanel = new UserMessagePanel(project, message, this);
|
||||
var attachedFilePath = CodeGPTKeys.IMAGE_ATTACHMENT_FILE_PATH.get(project);
|
||||
var callParameters = getCallParameters(conversationType, message, attachedFilePath);
|
||||
var callParameters =
|
||||
getCallParameters(conversationType, message, highlightedText, attachedFilePath);
|
||||
if (callParameters.getImageData() != null) {
|
||||
message.setImageFilePath(attachedFilePath);
|
||||
chatToolWindowPanel.ifPresent(panel -> panel.clearNotifications(project));
|
||||
|
|
@ -148,7 +156,7 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
var messagePanel = toolWindowScrollablePanel.addMessage(message.getId());
|
||||
messagePanel.add(userMessagePanel);
|
||||
|
||||
var responsePanel = createResponsePanel(message, conversationType);
|
||||
var responsePanel = createResponsePanel(callParameters, conversationType);
|
||||
messagePanel.add(responsePanel);
|
||||
call(callParameters, responsePanel);
|
||||
});
|
||||
|
|
@ -157,8 +165,10 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
private CallParameters getCallParameters(
|
||||
ConversationType conversationType,
|
||||
Message message,
|
||||
@Nullable String highlightedText,
|
||||
@Nullable String attachedFilePath) {
|
||||
var callParameters = new CallParameters(conversation, conversationType, message, false);
|
||||
var callParameters = new CallParameters(conversation, conversationType, message,
|
||||
highlightedText, false);
|
||||
if (attachedFilePath != null && !attachedFilePath.isEmpty()) {
|
||||
try {
|
||||
callParameters.setImageData(Files.readAllBytes(Path.of(attachedFilePath)));
|
||||
|
|
@ -170,12 +180,20 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
return callParameters;
|
||||
}
|
||||
|
||||
private ResponsePanel createResponsePanel(Message message, ConversationType conversationType) {
|
||||
private ResponsePanel createResponsePanel(
|
||||
CallParameters callParameters,
|
||||
ConversationType conversationType) {
|
||||
var message = callParameters.getMessage();
|
||||
return new ResponsePanel()
|
||||
.withReloadAction(() -> reloadMessage(message, conversation, conversationType))
|
||||
.withDeleteAction(() -> removeMessage(message.getId(), conversation))
|
||||
.addContent(
|
||||
new ChatMessageResponseBody(project, true, false, message.isWebSearchIncluded(),
|
||||
new ChatMessageResponseBody(
|
||||
project,
|
||||
callParameters.getHighlightedText(),
|
||||
true,
|
||||
false,
|
||||
message.isWebSearchIncluded(),
|
||||
message.getDocumentationDetails() != null, this));
|
||||
}
|
||||
|
||||
|
|
@ -196,7 +214,8 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
if (responsePanel != null) {
|
||||
message.setResponse("");
|
||||
conversationService.saveMessage(conversation, message);
|
||||
call(new CallParameters(conversation, conversationType, message, true), responsePanel);
|
||||
call(new CallParameters(conversation, conversationType, message, null, true),
|
||||
responsePanel);
|
||||
}
|
||||
|
||||
totalTokensPanel.updateConversationTokens(conversation);
|
||||
|
|
@ -250,12 +269,14 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
private Unit handleSubmit(String text, List<AppliedActionInlay> appliedInlayActions) {
|
||||
var message = new Message(text);
|
||||
var editor = EditorUtil.getSelectedEditor(project);
|
||||
String highlightedText = null;
|
||||
if (editor != null) {
|
||||
var selectionModel = editor.getSelectionModel();
|
||||
var selectedText = selectionModel.getSelectedText();
|
||||
if (selectedText != null && !selectedText.isEmpty()) {
|
||||
var fileExtension = FileUtil.getFileExtension(editor.getVirtualFile().getName());
|
||||
message = new Message(text + format("%n```%s%n%s%n```", fileExtension, selectedText));
|
||||
highlightedText = selectedText;
|
||||
selectionModel.removeSelection();
|
||||
}
|
||||
}
|
||||
|
|
@ -280,7 +301,7 @@ public class ChatToolWindowTabPanel implements Disposable {
|
|||
CodeGPTKeys.ADDED_PERSONA.set(project, null);
|
||||
}
|
||||
|
||||
sendMessage(message, ConversationType.DEFAULT);
|
||||
sendMessage(message, ConversationType.DEFAULT, highlightedText);
|
||||
return Unit.INSTANCE;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -111,6 +111,7 @@ abstract class ToolWindowCompletionResponseEventListener implements
|
|||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
try {
|
||||
responsePanel.enableActions();
|
||||
responseContainer.enableActions();
|
||||
totalTokensPanel.updateUserPromptTokens(textArea.getText());
|
||||
totalTokensPanel.updateConversationTokens(callParameters.getConversation());
|
||||
} finally {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ 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.CopyAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.DiffAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.EditAction;
|
||||
|
|
@ -40,16 +42,19 @@ import java.awt.FlowLayout;
|
|||
import javax.swing.Box;
|
||||
import javax.swing.JPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
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,
|
||||
String code,
|
||||
String markdownLanguage,
|
||||
boolean readOnly,
|
||||
@Nullable String highlightedText,
|
||||
Disposable disposableParent) {
|
||||
super(new BorderLayout());
|
||||
setBorder(JBUI.Borders.empty(8, 0));
|
||||
|
|
@ -59,7 +64,6 @@ public class ResponseEditorPanel extends JPanel implements Disposable {
|
|||
project,
|
||||
findLanguageExtensionMapping(markdownLanguage).getValue(),
|
||||
StringUtil.convertLineSeparators(code));
|
||||
|
||||
var group = new DefaultActionGroup();
|
||||
group.add(new ReplaceCodeInMainEditorAction());
|
||||
String originalGroupId = ((EditorEx) editor).getContextMenuGroupId();
|
||||
|
|
@ -76,9 +80,22 @@ public class ResponseEditorPanel extends JPanel implements Disposable {
|
|||
findLanguageExtensionMapping(markdownLanguage).getValue());
|
||||
add(editor.getComponent(), BorderLayout.CENTER);
|
||||
|
||||
if (highlightedText != null && !highlightedText.isEmpty()) {
|
||||
directLinksPanel.setVisible(false);
|
||||
directLinksPanel.setBorder(JBUI.Borders.emptyTop(4));
|
||||
directLinksPanel.add(new CompareWithOriginalActionLink(project, editor, highlightedText));
|
||||
directLinksPanel.add(Box.createHorizontalStrut(8));
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -2,58 +2,40 @@ package ee.carlrobert.codegpt.toolwindow.chat.editor.actions;
|
|||
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.intellij.diff.DiffContentFactory;
|
||||
import com.intellij.diff.DiffDialogHints;
|
||||
import com.intellij.diff.DiffManager;
|
||||
import com.intellij.diff.requests.SimpleDiffRequest;
|
||||
import com.intellij.diff.util.DiffUserDataKeys;
|
||||
import com.intellij.diff.util.DiffUtil;
|
||||
import com.intellij.diff.util.Side;
|
||||
import com.intellij.icons.AllIcons.Actions;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.ui.OverlayUtil;
|
||||
import ee.carlrobert.codegpt.util.EditorDiffUtil;
|
||||
import ee.carlrobert.codegpt.util.EditorUtil;
|
||||
import ee.carlrobert.codegpt.util.file.FileUtil;
|
||||
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 DiffAction extends AbstractAction {
|
||||
|
||||
private final EditorEx editor;
|
||||
private final EditorEx toolwindowEditor;
|
||||
private final Point locationOnScreen;
|
||||
|
||||
public DiffAction(@NotNull EditorEx editor, @NotNull Point locationOnScreen) {
|
||||
public DiffAction(EditorEx toolwindowEditor, @Nullable Point locationOnScreen) {
|
||||
super("Diff", Actions.DiffWithClipboard);
|
||||
this.editor = editor;
|
||||
this.toolwindowEditor = toolwindowEditor;
|
||||
this.locationOnScreen = locationOnScreen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
var project = requireNonNull(editor.getProject());
|
||||
var selectedTextEditor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (!EditorUtil.hasSelection(selectedTextEditor)) {
|
||||
var project = requireNonNull(toolwindowEditor.getProject());
|
||||
var mainEditor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (mainEditor != null && !EditorUtil.hasSelection(mainEditor) && locationOnScreen != null) {
|
||||
OverlayUtil.showSelectedEditorSelectionWarning(project, locationOnScreen);
|
||||
return;
|
||||
}
|
||||
|
||||
var resultEditorFile = FileUtil.getEditorFile(selectedTextEditor);
|
||||
var diffContentFactory = DiffContentFactory.getInstance();
|
||||
var request = new SimpleDiffRequest(
|
||||
CodeGPTBundle.get("editor.diff.title"),
|
||||
diffContentFactory.create(project, FileUtil.getEditorFile(editor)),
|
||||
diffContentFactory.create(project, resultEditorFile),
|
||||
CodeGPTBundle.get("editor.diff.local.content.title"),
|
||||
resultEditorFile.getName());
|
||||
request.putUserData(
|
||||
DiffUserDataKeys.SCROLL_TO_LINE,
|
||||
Pair.create(Side.RIGHT, DiffUtil.getCaretPosition(selectedTextEditor).line));
|
||||
|
||||
DiffManager.getInstance().showDiff(project, request, DiffDialogHints.DEFAULT);
|
||||
EditorDiffUtil.showDiff(
|
||||
project,
|
||||
toolwindowEditor,
|
||||
mainEditor.getSelectionModel().getSelectedText());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ import javax.swing.Icon;
|
|||
import javax.swing.JPanel;
|
||||
import javax.swing.JTextPane;
|
||||
import javax.swing.SwingConstants;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ChatMessageResponseBody extends JPanel {
|
||||
|
||||
|
|
@ -58,24 +59,19 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
private final WebpageList webpageList = new WebpageList(webpageListModel);
|
||||
private final JPanel webDocProgressContainer = new JPanel();
|
||||
private final AsyncProcessIcon spinner = new AsyncProcessIcon("sign_in_spinner");
|
||||
private final @Nullable String highlightedText;
|
||||
private ResponseEditorPanel currentlyProcessedEditorPanel;
|
||||
private JTextPane currentlyProcessedTextPane;
|
||||
private JPanel webpageListPanel;
|
||||
private boolean responseReceived;
|
||||
|
||||
public ChatMessageResponseBody(Project project, Disposable parentDisposable) {
|
||||
this(project, false, parentDisposable);
|
||||
}
|
||||
|
||||
public ChatMessageResponseBody(
|
||||
Project project,
|
||||
boolean withGhostText,
|
||||
Disposable parentDisposable) {
|
||||
this(project, withGhostText, false, false, false, parentDisposable);
|
||||
this(project, null, false, false, false, false, parentDisposable);
|
||||
}
|
||||
|
||||
public ChatMessageResponseBody(
|
||||
Project project,
|
||||
@Nullable String highlightedText,
|
||||
boolean withGhostText,
|
||||
boolean readOnly,
|
||||
boolean webSearchIncluded,
|
||||
|
|
@ -83,6 +79,7 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
Disposable parentDisposable) {
|
||||
super(new BorderLayout());
|
||||
this.project = project;
|
||||
this.highlightedText = highlightedText;
|
||||
this.parentDisposable = parentDisposable;
|
||||
this.streamParser = new StreamParser();
|
||||
this.readOnly = readOnly;
|
||||
|
|
@ -107,6 +104,14 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
public void enableActions() {
|
||||
if (highlightedText != null
|
||||
&& !highlightedText.isEmpty()
|
||||
&& currentlyProcessedEditorPanel != null) {
|
||||
currentlyProcessedEditorPanel.showEditorActions();
|
||||
}
|
||||
}
|
||||
|
||||
public ChatMessageResponseBody withResponse(String response) {
|
||||
for (var message : MarkdownUtil.splitCodeBlocks(response)) {
|
||||
processResponse(message, message.startsWith("```"), false);
|
||||
|
|
@ -265,6 +270,11 @@ 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);
|
||||
|
|
@ -274,7 +284,8 @@ public class ChatMessageResponseBody extends JPanel {
|
|||
hideCaret();
|
||||
currentlyProcessedTextPane = null;
|
||||
currentlyProcessedEditorPanel =
|
||||
new ResponseEditorPanel(project, code, markdownLanguage, readOnly, parentDisposable);
|
||||
new ResponseEditorPanel(project, code, markdownLanguage, readOnly, highlightedText,
|
||||
parentDisposable);
|
||||
add(currentlyProcessedEditorPanel);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ public class UserMessagePanel extends JPanel {
|
|||
Disposable parentDisposable) {
|
||||
return new ChatMessageResponseBody(
|
||||
project,
|
||||
null,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue