mirror of
https://github.com/carlrobertoh/ProxyAI.git
synced 2026-05-12 22:31:24 +00:00
210 - Add diff function (#213)
* Add diff function * modify writeSpace * Remove unnecessary code * Add telemetry for toolwindow editor actions --------- Co-authored-by: wang <1625116638@qq.com> Co-authored-by: Carl-Robert Linnupuu <carlrobertoh@gmail.com>
This commit is contained in:
parent
a2738dac0f
commit
8269ba371c
14 changed files with 488 additions and 274 deletions
|
|
@ -8,5 +8,10 @@ public enum ActionType {
|
|||
DELETE_CONVERSATION,
|
||||
DISCARD_TOKEN_LIMIT,
|
||||
OPEN_CONVERSATION_IN_EDITOR,
|
||||
DIFF_CODE,
|
||||
EDIT_CODE,
|
||||
CREATE_NEW_FILE,
|
||||
COPY_CODE,
|
||||
REPLACE_IN_MAIN_EDITOR,
|
||||
RELOAD_MESSAGE
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,268 +0,0 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat;
|
||||
|
||||
import static com.intellij.openapi.ui.DialogWrapper.OK_EXIT_CODE;
|
||||
import static ee.carlrobert.codegpt.util.FileUtils.findFileNameExtensionMapping;
|
||||
import static java.lang.String.format;
|
||||
|
||||
import com.intellij.icons.AllIcons.Actions;
|
||||
import com.intellij.ide.util.EditorHelper;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.EditorKind;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.ContextMenuPopupHandler;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogBuilder;
|
||||
import com.intellij.openapi.ui.TextBrowseFolderListener;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
import com.intellij.util.ui.FormBuilder;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.actions.toolwindow.ReplaceCodeInMainEditorAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.IconActionButton;
|
||||
import ee.carlrobert.codegpt.util.EditorUtils;
|
||||
import ee.carlrobert.codegpt.util.FileUtils;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Map;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ChatToolWindowTabPanelEditor implements Disposable {
|
||||
|
||||
private final Project project;
|
||||
private final Editor editor;
|
||||
private final Map.Entry<String, String> fileNameExtensionMapping;
|
||||
|
||||
public ChatToolWindowTabPanelEditor(
|
||||
Project project,
|
||||
String code,
|
||||
String language,
|
||||
Disposable disposableParent) {
|
||||
this.project = project;
|
||||
this.fileNameExtensionMapping = findFileNameExtensionMapping(language);
|
||||
|
||||
var timestamp = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
|
||||
var fileName = "temp_" + timestamp + fileNameExtensionMapping.getValue();
|
||||
var lightVirtualFile = new LightVirtualFile(
|
||||
format("%s/%s", PathManager.getTempPath(), fileName), code);
|
||||
var document = FileDocumentManager.getInstance().getDocument(lightVirtualFile);
|
||||
if (document == null) {
|
||||
document = EditorFactory.getInstance().createDocument(code);
|
||||
}
|
||||
EditorUtils.disableHighlighting(project, document);
|
||||
editor = EditorFactory.getInstance().createEditor(
|
||||
document,
|
||||
project,
|
||||
lightVirtualFile,
|
||||
true,
|
||||
EditorKind.UNTYPED);
|
||||
|
||||
DefaultActionGroup group = new DefaultActionGroup();
|
||||
group.add(new ReplaceCodeInMainEditorAction());
|
||||
|
||||
String originalGroupId = ((EditorEx) editor).getContextMenuGroupId();
|
||||
if (originalGroupId != null) {
|
||||
AnAction originalGroup = ActionManager.getInstance().getAction(originalGroupId);
|
||||
if (originalGroup instanceof ActionGroup) {
|
||||
group.addAll(((ActionGroup) originalGroup).getChildren(null));
|
||||
}
|
||||
}
|
||||
|
||||
var editorEx = ((EditorEx) editor);
|
||||
editorEx.installPopupHandler(new ContextMenuPopupHandler.Simple(group));
|
||||
editorEx.setColorsScheme(EditorColorsManager.getInstance().getSchemeForCurrentUITheme());
|
||||
|
||||
var settings = editor.getSettings();
|
||||
settings.setAdditionalColumnsCount(0);
|
||||
settings.setAdditionalLinesCount(0);
|
||||
settings.setAdditionalPageAtBottom(false);
|
||||
settings.setVirtualSpace(false);
|
||||
settings.setUseSoftWraps(false);
|
||||
|
||||
Disposer.register(disposableParent, this);
|
||||
}
|
||||
|
||||
public JComponent getComponent() {
|
||||
var wrapper = new JPanel(new BorderLayout());
|
||||
wrapper.add(createHeaderComponent(fileNameExtensionMapping.getKey()), BorderLayout.NORTH);
|
||||
wrapper.add(editor.getComponent(), BorderLayout.SOUTH);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public Editor getEditor() {
|
||||
return editor;
|
||||
}
|
||||
|
||||
private JPanel createHeaderComponent(String language) {
|
||||
var headerComponent = new JPanel(new BorderLayout());
|
||||
headerComponent.setBorder(JBUI.Borders.compound(
|
||||
JBUI.Borders.customLine(JBColor.border(), 1, 1, 1, 1),
|
||||
JBUI.Borders.empty(8)));
|
||||
headerComponent.add(new JBLabel(language), BorderLayout.LINE_START);
|
||||
headerComponent.add(createHeaderActions(), BorderLayout.LINE_END);
|
||||
return headerComponent;
|
||||
}
|
||||
|
||||
private JPanel createHeaderActions() {
|
||||
var wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
wrapper.add(new IconActionButton(new EditAction()));
|
||||
wrapper.add(Box.createHorizontalStrut(8));
|
||||
wrapper.add(new IconActionButton(new NewFileAction()));
|
||||
wrapper.add(Box.createHorizontalStrut(8));
|
||||
wrapper.add(new IconActionButton(new CopyAction()));
|
||||
wrapper.add(Box.createHorizontalStrut(8));
|
||||
wrapper.add(new IconActionButton(new ReplaceInMainEditorAction()));
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
EditorFactory.getInstance().releaseEditor(editor);
|
||||
}
|
||||
|
||||
class EditAction extends AnAction {
|
||||
|
||||
EditAction() {
|
||||
super("Edit Source", "Edit Source description", Actions.EditSource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent event) {
|
||||
var editorEx = ((EditorEx) editor);
|
||||
editorEx.setViewer(!editorEx.isViewer());
|
||||
|
||||
var viewer = editorEx.isViewer();
|
||||
editorEx.setCaretVisible(!viewer);
|
||||
editorEx.setCaretEnabled(!viewer);
|
||||
|
||||
var settings = editorEx.getSettings();
|
||||
settings.setCaretRowShown(!viewer);
|
||||
|
||||
event.getPresentation().setIcon(viewer ? Actions.EditSource : Actions.Show);
|
||||
event.getPresentation().setText(viewer ? "Edit Source" : "Disable Editing");
|
||||
|
||||
var locationOnScreen = ((MouseEvent) event.getInputEvent()).getLocationOnScreen();
|
||||
locationOnScreen.y = locationOnScreen.y - 16;
|
||||
}
|
||||
}
|
||||
|
||||
class NewFileAction extends AnAction {
|
||||
|
||||
NewFileAction() {
|
||||
super("New File", "New File description", Actions.AddFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
var fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
|
||||
fileChooserDescriptor.setForcedToUseIdeaFileChooser(true);
|
||||
var textFieldWithBrowseButton = new TextFieldWithBrowseButton();
|
||||
textFieldWithBrowseButton.addBrowseFolderListener(
|
||||
new TextBrowseFolderListener(fileChooserDescriptor, project));
|
||||
var fileNameTextField = new JBTextField("Untitled" + fileNameExtensionMapping.getValue());
|
||||
fileNameTextField.setColumns(30);
|
||||
|
||||
if (showDialog(textFieldWithBrowseButton, fileNameTextField) == OK_EXIT_CODE) {
|
||||
var file = FileUtils.createFile(
|
||||
textFieldWithBrowseButton.getText(),
|
||||
fileNameTextField.getText(),
|
||||
editor.getDocument().getText());
|
||||
var virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
|
||||
if (virtualFile == null) {
|
||||
throw new RuntimeException("Couldn't find the saved virtual file");
|
||||
}
|
||||
var psiFile = PsiManager.getInstance(project).findFile(virtualFile);
|
||||
if (psiFile == null) {
|
||||
throw new RuntimeException("Couldn't find the saved psi file");
|
||||
}
|
||||
|
||||
EditorHelper.openInEditor(psiFile);
|
||||
}
|
||||
}
|
||||
|
||||
private int showDialog(
|
||||
TextFieldWithBrowseButton textFieldWithBrowseButton,
|
||||
JBTextField fileNameTextField) {
|
||||
var dialogBuilder = new DialogBuilder(project)
|
||||
.title("New File")
|
||||
.centerPanel(FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent("File name:", fileNameTextField)
|
||||
.addLabeledComponent("Destination:", textFieldWithBrowseButton)
|
||||
.getPanel());
|
||||
dialogBuilder.addOkAction();
|
||||
dialogBuilder.addCancelAction();
|
||||
return dialogBuilder.show();
|
||||
}
|
||||
}
|
||||
|
||||
class CopyAction extends AnAction {
|
||||
|
||||
CopyAction() {
|
||||
super("Copy", "Copy description", Actions.Copy);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent event) {
|
||||
StringSelection stringSelection = new StringSelection(editor.getDocument().getText());
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
clipboard.setContents(stringSelection, null);
|
||||
|
||||
var locationOnScreen = ((MouseEvent) event.getInputEvent()).getLocationOnScreen();
|
||||
locationOnScreen.y = locationOnScreen.y - 16;
|
||||
|
||||
OverlayUtils.showInfoBalloon("Code copied!", locationOnScreen);
|
||||
}
|
||||
}
|
||||
|
||||
class ReplaceInMainEditorAction extends AnAction {
|
||||
|
||||
ReplaceInMainEditorAction() {
|
||||
super("Replace in Main Editor", "Replace in Main Editor description", Actions.Replace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent event) {
|
||||
var project = event.getProject();
|
||||
if (project != null) {
|
||||
if (EditorUtils.isMainEditorTextSelected(project)) {
|
||||
EditorUtils.replaceMainEditorSelection(project, editor.getDocument().getText());
|
||||
} else {
|
||||
var locationOnScreen = ((MouseEvent) event.getInputEvent()).getLocationOnScreen();
|
||||
locationOnScreen.y = locationOnScreen.y - 16;
|
||||
|
||||
OverlayUtils.showWarningBalloon(
|
||||
EditorUtils.getSelectedEditor(project) == null
|
||||
? "Unable to locate a selected editor"
|
||||
: "Please select a target code before proceeding",
|
||||
locationOnScreen);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,7 @@ import com.vladsch.flexmark.util.data.MutableDataSet;
|
|||
import ee.carlrobert.codegpt.completions.SerpResult;
|
||||
import ee.carlrobert.codegpt.settings.SettingsConfigurable;
|
||||
import ee.carlrobert.codegpt.settings.state.SettingsState;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ChatToolWindowTabPanelEditor;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.editor.ChatToolWindowTabPanelEditor;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.ResponseNodeRenderer;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.StreamParser;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.StreamResponseType;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat.editor;
|
||||
|
||||
import static ee.carlrobert.codegpt.util.FileUtils.findFileNameExtensionMapping;
|
||||
import static java.lang.String.format;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.actionSystem.ActionGroup;
|
||||
import com.intellij.openapi.actionSystem.ActionManager;
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.DefaultActionGroup;
|
||||
import com.intellij.openapi.application.PathManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.editor.EditorFactory;
|
||||
import com.intellij.openapi.editor.EditorKind;
|
||||
import com.intellij.openapi.editor.colors.EditorColorsManager;
|
||||
import com.intellij.openapi.editor.ex.EditorEx;
|
||||
import com.intellij.openapi.editor.impl.ContextMenuPopupHandler;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Disposer;
|
||||
import com.intellij.testFramework.LightVirtualFile;
|
||||
import com.intellij.ui.JBColor;
|
||||
import com.intellij.ui.components.JBLabel;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import ee.carlrobert.codegpt.actions.toolwindow.ReplaceCodeInMainEditorAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.IconActionButton;
|
||||
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.NewFileAction;
|
||||
import ee.carlrobert.codegpt.toolwindow.chat.editor.actions.ReplaceSelectionAction;
|
||||
import ee.carlrobert.codegpt.util.EditorUtils;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.FlowLayout;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JPanel;
|
||||
|
||||
public class ChatToolWindowTabPanelEditor implements Disposable {
|
||||
|
||||
private final Editor editor;
|
||||
private final String fileName;
|
||||
private final String fileExtension;
|
||||
|
||||
public ChatToolWindowTabPanelEditor(
|
||||
Project project,
|
||||
String code,
|
||||
String language,
|
||||
Disposable disposableParent) {
|
||||
var fileNameExtensionMapping = findFileNameExtensionMapping(language);
|
||||
this.fileName = fileNameExtensionMapping.getKey();
|
||||
this.fileExtension = fileNameExtensionMapping.getValue();
|
||||
|
||||
var timestamp = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now());
|
||||
var fileName = "temp_" + timestamp + fileExtension;
|
||||
var lightVirtualFile = new LightVirtualFile(
|
||||
format("%s/%s", PathManager.getTempPath(), fileName), code);
|
||||
var document = FileDocumentManager.getInstance().getDocument(lightVirtualFile);
|
||||
if (document == null) {
|
||||
document = EditorFactory.getInstance().createDocument(code);
|
||||
}
|
||||
EditorUtils.disableHighlighting(project, document);
|
||||
editor = EditorFactory.getInstance().createEditor(
|
||||
document,
|
||||
project,
|
||||
lightVirtualFile,
|
||||
true,
|
||||
EditorKind.UNTYPED);
|
||||
|
||||
DefaultActionGroup group = new DefaultActionGroup();
|
||||
group.add(new ReplaceCodeInMainEditorAction());
|
||||
|
||||
String originalGroupId = ((EditorEx) editor).getContextMenuGroupId();
|
||||
if (originalGroupId != null) {
|
||||
AnAction originalGroup = ActionManager.getInstance().getAction(originalGroupId);
|
||||
if (originalGroup instanceof ActionGroup) {
|
||||
group.addAll(((ActionGroup) originalGroup).getChildren(null));
|
||||
}
|
||||
}
|
||||
|
||||
var editorEx = ((EditorEx) editor);
|
||||
editorEx.installPopupHandler(new ContextMenuPopupHandler.Simple(group));
|
||||
editorEx.setColorsScheme(EditorColorsManager.getInstance().getSchemeForCurrentUITheme());
|
||||
|
||||
var settings = editor.getSettings();
|
||||
settings.setAdditionalColumnsCount(0);
|
||||
settings.setAdditionalLinesCount(0);
|
||||
settings.setAdditionalPageAtBottom(false);
|
||||
settings.setVirtualSpace(false);
|
||||
settings.setUseSoftWraps(false);
|
||||
|
||||
Disposer.register(disposableParent, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
EditorFactory.getInstance().releaseEditor(editor);
|
||||
}
|
||||
|
||||
public JComponent getComponent() {
|
||||
var wrapper = new JPanel(new BorderLayout());
|
||||
wrapper.add(createHeaderComponent(), BorderLayout.NORTH);
|
||||
wrapper.add(editor.getComponent(), BorderLayout.SOUTH);
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
public Editor getEditor() {
|
||||
return editor;
|
||||
}
|
||||
|
||||
private JPanel createHeaderComponent() {
|
||||
var headerComponent = new JPanel(new BorderLayout());
|
||||
headerComponent.setBorder(JBUI.Borders.compound(
|
||||
JBUI.Borders.customLine(JBColor.border(), 1, 1, 1, 1),
|
||||
JBUI.Borders.empty(8)));
|
||||
headerComponent.add(new JBLabel(fileName), BorderLayout.LINE_START);
|
||||
headerComponent.add(createHeaderActions(), BorderLayout.LINE_END);
|
||||
return headerComponent;
|
||||
}
|
||||
|
||||
private JPanel createHeaderActions() {
|
||||
var wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0));
|
||||
wrapper.add(new IconActionButton(new DiffAction(editor)));
|
||||
wrapper.add(Box.createHorizontalStrut(8));
|
||||
wrapper.add(new IconActionButton(new EditAction(editor)));
|
||||
wrapper.add(Box.createHorizontalStrut(8));
|
||||
wrapper.add(new IconActionButton(new NewFileAction(editor, fileExtension)));
|
||||
wrapper.add(Box.createHorizontalStrut(8));
|
||||
wrapper.add(new IconActionButton(new CopyAction(editor)));
|
||||
wrapper.add(Box.createHorizontalStrut(8));
|
||||
wrapper.add(new IconActionButton(new ReplaceSelectionAction(editor)));
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat.editor.actions;
|
||||
|
||||
import com.intellij.icons.AllIcons.Actions;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.actions.ActionType;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.awt.event.MouseEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class CopyAction extends TrackableAction {
|
||||
|
||||
public CopyAction(@NotNull Editor editor) {
|
||||
super(
|
||||
editor,
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.copy.title"),
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.copy.description"),
|
||||
Actions.Copy,
|
||||
ActionType.COPY_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAction(@NotNull AnActionEvent event) {
|
||||
StringSelection stringSelection = new StringSelection(editor.getDocument().getText());
|
||||
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
|
||||
clipboard.setContents(stringSelection, null);
|
||||
|
||||
var locationOnScreen = ((MouseEvent) event.getInputEvent()).getLocationOnScreen();
|
||||
locationOnScreen.y = locationOnScreen.y - 16;
|
||||
|
||||
OverlayUtils.showInfoBalloon("Code copied!", locationOnScreen);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
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.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.actions.ActionType;
|
||||
import ee.carlrobert.codegpt.util.EditorUtils;
|
||||
import ee.carlrobert.codegpt.util.FileUtils;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class DiffAction extends TrackableAction {
|
||||
|
||||
public DiffAction(@NotNull Editor editor) {
|
||||
super(
|
||||
editor,
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.diff.title"),
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.diff.description"),
|
||||
Actions.DiffWithClipboard,
|
||||
ActionType.DIFF_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAction(@NotNull AnActionEvent event) {
|
||||
var project = requireNonNull(event.getProject());
|
||||
var selectedTextEditor = FileEditorManager.getInstance(project).getSelectedTextEditor();
|
||||
if (!EditorUtils.hasSelection(selectedTextEditor)) {
|
||||
OverlayUtils.showSelectedEditorSelectionWarning(event);
|
||||
return;
|
||||
}
|
||||
|
||||
var resultEditorFile = FileUtils.getEditorFile(selectedTextEditor);
|
||||
var diffContentFactory = DiffContentFactory.getInstance();
|
||||
var request = new SimpleDiffRequest(
|
||||
CodeGPTBundle.get("editor.diff.title"),
|
||||
diffContentFactory.create(project, FileUtils.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);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat.editor.actions;
|
||||
|
||||
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 java.awt.event.MouseEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class EditAction extends TrackableAction {
|
||||
|
||||
public EditAction(@NotNull Editor editor) {
|
||||
super(
|
||||
editor,
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.edit.title"),
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.edit.description"),
|
||||
Actions.EditSource,
|
||||
ActionType.EDIT_CODE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAction(@NotNull AnActionEvent event) {
|
||||
var editorEx = ((EditorEx) editor);
|
||||
editorEx.setViewer(!editorEx.isViewer());
|
||||
|
||||
var viewer = editorEx.isViewer();
|
||||
editorEx.setCaretVisible(!viewer);
|
||||
editorEx.setCaretEnabled(!viewer);
|
||||
|
||||
var settings = editorEx.getSettings();
|
||||
settings.setCaretRowShown(!viewer);
|
||||
|
||||
event.getPresentation().setIcon(viewer ? Actions.EditSource : Actions.Show);
|
||||
event.getPresentation().setText(viewer ? "Edit Source" : "Disable Editing");
|
||||
|
||||
var locationOnScreen = ((MouseEvent) event.getInputEvent()).getLocationOnScreen();
|
||||
locationOnScreen.y = locationOnScreen.y - 16;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat.editor.actions;
|
||||
|
||||
import static com.intellij.openapi.ui.DialogWrapper.OK_EXIT_CODE;
|
||||
|
||||
import com.intellij.icons.AllIcons.Actions;
|
||||
import com.intellij.ide.util.EditorHelper;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogBuilder;
|
||||
import com.intellij.openapi.ui.TextBrowseFolderListener;
|
||||
import com.intellij.openapi.ui.TextFieldWithBrowseButton;
|
||||
import com.intellij.openapi.vfs.LocalFileSystem;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.ui.components.JBTextField;
|
||||
import com.intellij.util.ui.FormBuilder;
|
||||
import ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.actions.ActionType;
|
||||
import ee.carlrobert.codegpt.util.FileUtils;
|
||||
import java.util.Objects;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class NewFileAction extends TrackableAction {
|
||||
|
||||
private final String fileExtension;
|
||||
|
||||
public NewFileAction(@NotNull Editor editor, String fileExtension) {
|
||||
super(
|
||||
editor,
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.newFile.title"),
|
||||
CodeGPTBundle.get("toolwindow.chat.editor.action.newFile.description"),
|
||||
Actions.AddFile,
|
||||
ActionType.CREATE_NEW_FILE);
|
||||
this.fileExtension = fileExtension;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAction(@NotNull AnActionEvent e) {
|
||||
var project = Objects.requireNonNull(e.getProject());
|
||||
var fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor();
|
||||
fileChooserDescriptor.setForcedToUseIdeaFileChooser(true);
|
||||
var textFieldWithBrowseButton = new TextFieldWithBrowseButton();
|
||||
textFieldWithBrowseButton.addBrowseFolderListener(
|
||||
new TextBrowseFolderListener(fileChooserDescriptor, project));
|
||||
var fileNameTextField = new JBTextField("Untitled" + fileExtension);
|
||||
fileNameTextField.setColumns(30);
|
||||
|
||||
if (showDialog(project, textFieldWithBrowseButton, fileNameTextField) == OK_EXIT_CODE) {
|
||||
var file = FileUtils.createFile(
|
||||
textFieldWithBrowseButton.getText(),
|
||||
fileNameTextField.getText(),
|
||||
editor.getDocument().getText());
|
||||
var virtualFile = LocalFileSystem.getInstance().refreshAndFindFileByIoFile(file);
|
||||
if (virtualFile == null) {
|
||||
throw new RuntimeException("Couldn't find the saved virtual file");
|
||||
}
|
||||
var psiFile = PsiManager.getInstance(project).findFile(virtualFile);
|
||||
if (psiFile == null) {
|
||||
throw new RuntimeException("Couldn't find the saved psi file");
|
||||
}
|
||||
|
||||
EditorHelper.openInEditor(psiFile);
|
||||
}
|
||||
}
|
||||
|
||||
private int showDialog(
|
||||
Project project,
|
||||
TextFieldWithBrowseButton textFieldWithBrowseButton,
|
||||
JBTextField fileNameTextField) {
|
||||
var dialogBuilder = new DialogBuilder(project)
|
||||
.title("New File")
|
||||
.centerPanel(FormBuilder.createFormBuilder()
|
||||
.addLabeledComponent("File name:", fileNameTextField)
|
||||
.addLabeledComponent("Destination:", textFieldWithBrowseButton)
|
||||
.getPanel());
|
||||
dialogBuilder.addOkAction();
|
||||
dialogBuilder.addCancelAction();
|
||||
return dialogBuilder.show();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
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 ee.carlrobert.codegpt.CodeGPTBundle;
|
||||
import ee.carlrobert.codegpt.actions.ActionType;
|
||||
import ee.carlrobert.codegpt.util.EditorUtils;
|
||||
import ee.carlrobert.codegpt.util.OverlayUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ReplaceSelectionAction extends TrackableAction {
|
||||
|
||||
public ReplaceSelectionAction(@NotNull Editor editor) {
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleAction(@NotNull AnActionEvent event) {
|
||||
var project = requireNonNull(event.getProject());
|
||||
if (EditorUtils.isMainEditorTextSelected(project)) {
|
||||
EditorUtils.replaceMainEditorSelection(project, editor.getDocument().getText());
|
||||
} else {
|
||||
OverlayUtils.showSelectedEditorSelectionWarning(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package ee.carlrobert.codegpt.toolwindow.chat.editor.actions;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnAction;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import ee.carlrobert.codegpt.actions.ActionType;
|
||||
import ee.carlrobert.codegpt.telemetry.TelemetryAction;
|
||||
import javax.swing.Icon;
|
||||
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;
|
||||
}
|
||||
|
||||
abstract void handleAction(@NotNull AnActionEvent e);
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
try {
|
||||
handleAction(e);
|
||||
} catch (Exception ex) {
|
||||
TelemetryAction.IDE_ACTION_ERROR
|
||||
.createActionMessage()
|
||||
.error(ex)
|
||||
.send();
|
||||
throw ex;
|
||||
} finally {
|
||||
TelemetryAction.IDE_ACTION
|
||||
.createActionMessage()
|
||||
.property("group", null)
|
||||
.property("action", actionType.name())
|
||||
.send();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,10 @@ import com.fasterxml.jackson.core.JsonProcessingException;
|
|||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
|
|
@ -19,6 +22,7 @@ import java.util.Objects;
|
|||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class FileUtils {
|
||||
|
||||
|
|
@ -27,12 +31,19 @@ public class FileUtils {
|
|||
public static File createFile(String directoryPath, String fileName, String fileContent) {
|
||||
try {
|
||||
tryCreateDirectory(directoryPath);
|
||||
return Files.writeString(Path.of(directoryPath, fileName), fileContent, StandardOpenOption.CREATE).toFile();
|
||||
return Files.writeString(
|
||||
Path.of(directoryPath, fileName),
|
||||
fileContent,
|
||||
StandardOpenOption.CREATE).toFile();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to create file", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static VirtualFile getEditorFile(@NotNull Editor editor) {
|
||||
return FileDocumentManager.getInstance().getFile(editor.getDocument());
|
||||
}
|
||||
|
||||
public static void tryCreateDirectory(String directoryPath) {
|
||||
try {
|
||||
if (!FileUtil.exists(directoryPath)) {
|
||||
|
|
@ -63,8 +74,12 @@ public class FileUtils {
|
|||
List<FileExtensionLanguageDetails> fileExtensionLanguageMappings;
|
||||
List<LanguageFileExtensionDetails> languageFileExtensionMappings;
|
||||
try {
|
||||
fileExtensionLanguageMappings = mapper.readValue(getResourceContent("/fileExtensionLanguageMappings.json"), new TypeReference<>() {});
|
||||
languageFileExtensionMappings = mapper.readValue(getResourceContent("/languageFileExtensionMappings.json"), new TypeReference<>() {});
|
||||
fileExtensionLanguageMappings = mapper.readValue(
|
||||
getResourceContent("/fileExtensionLanguageMappings.json"), new TypeReference<>() {
|
||||
});
|
||||
languageFileExtensionMappings = mapper.readValue(
|
||||
getResourceContent("/languageFileExtensionMappings.json"), new TypeReference<>() {
|
||||
});
|
||||
} catch (JsonProcessingException e) {
|
||||
LOG.error("Unable to extract file extension", e);
|
||||
return defaultValue;
|
||||
|
|
@ -74,7 +89,8 @@ public class FileUtils {
|
|||
.orElseGet(() -> fileExtensionLanguageMappings.stream()
|
||||
.filter(it -> it.getExtension().equalsIgnoreCase(language))
|
||||
.findFirst()
|
||||
.map(it -> findFirstExtension(languageFileExtensionMappings, it.getValue()).orElse(defaultValue))
|
||||
.map(it -> findFirstExtension(languageFileExtensionMappings, it.getValue())
|
||||
.orElse(defaultValue))
|
||||
.orElse(defaultValue));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@ package ee.carlrobert.codegpt.util;
|
|||
import static com.intellij.openapi.ui.Messages.CANCEL;
|
||||
import static com.intellij.openapi.ui.Messages.OK;
|
||||
import static ee.carlrobert.codegpt.Icons.DefaultIcon;
|
||||
import static java.util.Objects.requireNonNull;
|
||||
|
||||
import com.intellij.execution.ExecutionBundle;
|
||||
import com.intellij.notification.Notification;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.notification.Notifications;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.DialogBuilder;
|
||||
import com.intellij.openapi.ui.DoNotAskOption;
|
||||
|
|
@ -24,6 +26,7 @@ import ee.carlrobert.codegpt.CodeGPTBundle;
|
|||
import ee.carlrobert.codegpt.conversations.ConversationsState;
|
||||
import ee.carlrobert.codegpt.indexes.FolderStructureTreePanel;
|
||||
import java.awt.Point;
|
||||
import java.awt.event.MouseEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class OverlayUtils {
|
||||
|
|
@ -88,6 +91,17 @@ public class OverlayUtils {
|
|||
.guessWindowAndAsk() ? OK : CANCEL;
|
||||
}
|
||||
|
||||
public static void showSelectedEditorSelectionWarning(AnActionEvent event) {
|
||||
var locationOnScreen = ((MouseEvent) event.getInputEvent()).getLocationOnScreen();
|
||||
locationOnScreen.y = locationOnScreen.y - 16;
|
||||
|
||||
showWarningBalloon(
|
||||
EditorUtils.getSelectedEditor(requireNonNull(event.getProject())) == null
|
||||
? "Unable to locate a selected editor"
|
||||
: "Please select a target code before proceeding",
|
||||
locationOnScreen);
|
||||
}
|
||||
|
||||
public static void showWarningBalloon(String content, Point locationOnScreen) {
|
||||
showBalloon(content, MessageType.WARNING, locationOnScreen);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue