diff --git a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanelEditor.java b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanelEditor.java index 0f922444..58317e24 100644 --- a/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanelEditor.java +++ b/src/main/java/ee/carlrobert/codegpt/toolwindow/chat/ChatToolWindowTabPanelEditor.java @@ -3,6 +3,8 @@ package ee.carlrobert.codegpt.toolwindow.chat; import static ee.carlrobert.codegpt.util.FileUtils.findFileNameExtensionMapping; import com.intellij.icons.AllIcons; +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; @@ -16,16 +18,25 @@ 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; @@ -43,28 +54,45 @@ import org.jetbrains.annotations.NotNull; public class ChatToolWindowTabPanelEditor implements Disposable { + private final Project project; private final Editor editor; private final Map.Entry fileNameExtensionMapping; - public ChatToolWindowTabPanelEditor(Project project, String code, String language, Disposable disposableParent) { + public ChatToolWindowTabPanelEditor( + Project project, + String code, + String language, + Disposable disposableParent) { + this.project = project; this.fileNameExtensionMapping = findFileNameExtensionMapping(language); - var fileName = "temp_" + DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()) + fileNameExtensionMapping.getValue(); - var lightVirtualFile = new LightVirtualFile(String.format("%s/%s", PathManager.getTempPath(), fileName), code); + var timestamp = DateTimeFormatter.ofPattern("yyyyMMddHHmmss").format(LocalDateTime.now()); + var fileName = "temp_" + timestamp + fileNameExtensionMapping.getValue(); + var lightVirtualFile = new LightVirtualFile( + String.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); - String originalGroupId = ((EditorEx) editor).getContextMenuGroupId(); - AnAction originalGroup = originalGroupId == null ? null : ActionManager.getInstance().getAction(originalGroupId); + editor = EditorFactory.getInstance().createEditor( + document, + project, + lightVirtualFile, + true, + EditorKind.UNTYPED); + DefaultActionGroup group = new DefaultActionGroup(); - if (originalGroup instanceof ActionGroup) { - group.addAll(((ActionGroup) originalGroup).getChildren(null)); - } 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()); @@ -102,44 +130,105 @@ public class ChatToolWindowTabPanelEditor implements Disposable { private JPanel createHeaderActions() { var wrapper = new JPanel(new FlowLayout(FlowLayout.LEFT, 0, 0)); - wrapper.add(new IconActionButton("Copy", AllIcons.Actions.Copy, new AnAction() { - @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); - } - })); + wrapper.add(new IconActionButton("New File", Actions.AddFile, new NewFileAction())); wrapper.add(Box.createHorizontalStrut(8)); - wrapper.add(new IconActionButton("Replace in Main Editor", AllIcons.Actions.Replace, new AnAction() { - @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); - } - } - } - })); + wrapper.add(new IconActionButton("Copy", AllIcons.Actions.Copy, new CopyAction())); + wrapper.add(Box.createHorizontalStrut(8)); + wrapper.add(new IconActionButton( + "Replace in Main Editor", + AllIcons.Actions.Replace, + new ReplaceInMainEditorAction())); return wrapper; } - @Override public void dispose() { EditorFactory.getInstance().releaseEditor(editor); } + + class NewFileAction extends AnAction { + + private final TextFieldWithBrowseButton textFieldWithBrowseButton; + private final JBTextField fileNameTextField; + + NewFileAction() { + var fileChooserDescriptor = FileChooserDescriptorFactory.createSingleFolderDescriptor(); + fileChooserDescriptor.setForcedToUseIdeaFileChooser(true); + + textFieldWithBrowseButton = new TextFieldWithBrowseButton(); + textFieldWithBrowseButton.addBrowseFolderListener( + new TextBrowseFolderListener(fileChooserDescriptor, project)); + fileNameTextField = new JBTextField("Untitled" + fileNameExtensionMapping.getValue()); + fileNameTextField.setColumns(30); + } + + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + if (getDialogBuilder().show() == 0) { + 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 DialogBuilder getDialogBuilder() { + var dialogBuilder = new DialogBuilder(project); + dialogBuilder.setTitle("New File"); + dialogBuilder.setCenterPanel(FormBuilder.createFormBuilder() + .addLabeledComponent("File name:", fileNameTextField) + .addLabeledComponent("Destination:", textFieldWithBrowseButton) + .getPanel()); + dialogBuilder.addOkAction(); + dialogBuilder.addCancelAction(); + return dialogBuilder; + } + } + + class CopyAction extends AnAction { + + @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 { + + @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); + } + } + } + } }