diff --git a/src/main/java/mp/code/intellij/actions/buffer/BufferShareAction.java b/src/main/java/mp/code/intellij/actions/buffer/BufferShareAction.java new file mode 100644 index 0000000..9242ca3 --- /dev/null +++ b/src/main/java/mp/code/intellij/actions/buffer/BufferShareAction.java @@ -0,0 +1,74 @@ +package mp.code.intellij.actions.buffer; + +import com.intellij.openapi.actionSystem.AnAction; +import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.fileEditor.FileEditor; +import com.intellij.openapi.fileEditor.FileEditorManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import mp.code.BufferController; +import mp.code.data.TextChange; +import mp.code.exceptions.ControllerException; +import mp.code.intellij.CodeMP; +import mp.code.intellij.util.FileUtil; +import mp.code.intellij.util.InteractionUtil; +import mp.code.intellij.util.cb.BufferCallback; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.util.Optional; +import java.util.OptionalLong; + +public class BufferShareAction extends AnAction { + @Override + public void actionPerformed(@NotNull AnActionEvent e) { + Project proj = e.getProject(); + FileEditor currentEditor = FileEditorManager.getInstance(proj).getSelectedEditor(); + if(currentEditor == null) { + Messages.showErrorDialog( + "No file is currently open!", + "CodeMP Buffer Share" + ); + return; + } + + String path = FileUtil.getRelativePath(proj, currentEditor.getFile()); + if(path == null) { + Messages.showErrorDialog( + "File must belong to project!", + "CodeMP Buffer Share" + ); + return; + } + + InteractionUtil.createBuffer(proj, path); + Optional controller = InteractionUtil.bufferAttach(proj, CodeMP.getActiveWorkspace(), path); + if(controller.isEmpty()) { + Messages.showErrorDialog( + "An unknown error has occurred!", + "CodeMP Buffer Share" + ); + return; + } + + try { + controller.get().send(new TextChange( + 0, + 0, + new String(currentEditor.getFile().contentsToByteArray()), + OptionalLong.empty() + )); + ApplicationManager.getApplication().runWriteAction(() -> { + try { + FileUtil.getAndRegisterBufferEquivalent(this, proj, controller.get()); + } catch(Exception ex) { + throw new RuntimeException(ex); + } + }); + controller.get().callback(buf -> new BufferCallback(proj).accept(buf)); + } catch(ControllerException | IOException ex) { + throw new RuntimeException(ex); + } + } +} diff --git a/src/main/java/mp/code/intellij/actions/workspace/WorkspaceDeleteAction.java b/src/main/java/mp/code/intellij/actions/workspace/WorkspaceDeleteAction.java index ed3595e..ebd508d 100644 --- a/src/main/java/mp/code/intellij/actions/workspace/WorkspaceDeleteAction.java +++ b/src/main/java/mp/code/intellij/actions/workspace/WorkspaceDeleteAction.java @@ -15,6 +15,7 @@ public class WorkspaceDeleteAction extends AnAction { "You do not own any workspaces. Ensure you own at least one!", "CodeMP Delete Workspace" ); + return; } int choice = Messages.showDialog( // TODO NOT THE ONE diff --git a/src/main/java/mp/code/intellij/actions/workspace/WorkspaceInviteAction.java b/src/main/java/mp/code/intellij/actions/workspace/WorkspaceInviteAction.java index 1ec8726..e58615b 100644 --- a/src/main/java/mp/code/intellij/actions/workspace/WorkspaceInviteAction.java +++ b/src/main/java/mp/code/intellij/actions/workspace/WorkspaceInviteAction.java @@ -19,6 +19,7 @@ public class WorkspaceInviteAction extends AnAction { "You do not own any workspaces. Ensure you own at least one!", "CodeMP Invite To Workspace" ); + return; } int choice = Messages.showDialog( // TODO NOT THE ONE diff --git a/src/main/java/mp/code/intellij/actions/workspace/WorkspaceJoinAction.java b/src/main/java/mp/code/intellij/actions/workspace/WorkspaceJoinAction.java index ead67be..ebe45b6 100644 --- a/src/main/java/mp/code/intellij/actions/workspace/WorkspaceJoinAction.java +++ b/src/main/java/mp/code/intellij/actions/workspace/WorkspaceJoinAction.java @@ -17,6 +17,7 @@ public class WorkspaceJoinAction extends AnAction { "There are no available workspaces. Ensure you have rights to access at least one!", "CodeMP Join Workspace" ); + return; } int choice = Messages.showDialog( // TODO NOT THE ONE diff --git a/src/main/java/mp/code/intellij/actions/workspace/WorkspaceLeaveAction.java b/src/main/java/mp/code/intellij/actions/workspace/WorkspaceLeaveAction.java index da2aa80..f41ee11 100644 --- a/src/main/java/mp/code/intellij/actions/workspace/WorkspaceLeaveAction.java +++ b/src/main/java/mp/code/intellij/actions/workspace/WorkspaceLeaveAction.java @@ -16,7 +16,7 @@ public class WorkspaceLeaveAction extends AnAction { "CodeMP Workspace Leave", Messages.getQuestionIcon()); - InteractionUtil.leaveWorkspace(e.getProject(), workspaceId); + InteractionUtil.leaveWorkspace(e.getProject(), workspaceId, null); } @Override diff --git a/src/main/java/mp/code/intellij/listeners/BufferEventListener.java b/src/main/java/mp/code/intellij/listeners/BufferEventListener.java index 50bbfae..cc2000c 100644 --- a/src/main/java/mp/code/intellij/listeners/BufferEventListener.java +++ b/src/main/java/mp/code/intellij/listeners/BufferEventListener.java @@ -13,6 +13,7 @@ import mp.code.data.TextChange; import org.jetbrains.annotations.NotNull; import java.util.Objects; +import java.util.Optional; import java.util.OptionalLong; public class BufferEventListener implements DocumentListener { @@ -37,19 +38,21 @@ public class BufferEventListener implements DocumentListener { if(file == null) return; - CodeMP.getActiveWorkspace().getBuffer(CodeMP.BUFFER_MAPPER.get(file.toNioPath())).ifPresent(controller -> { - int changeOffset = event.getOffset(); - CharSequence newFragment = event.getNewFragment(); - try { - controller.send(new TextChange( - changeOffset, - changeOffset + event.getOldFragment().length(), - newFragment.toString(), - OptionalLong.empty() - )); - } catch(ControllerException e) { - throw new RuntimeException(e); - } - }); + Optional.ofNullable(CodeMP.BUFFER_MAPPER.get(file.toNioPath())) + .flatMap(c -> CodeMP.getActiveWorkspace().getBuffer(c)) + .ifPresent(controller -> { + int changeOffset = event.getOffset(); + CharSequence newFragment = event.getNewFragment(); + try { + controller.send(new TextChange( + changeOffset, + changeOffset + event.getOldFragment().length(), + newFragment.toString(), + OptionalLong.empty() + )); + } catch(ControllerException e) { + throw new RuntimeException(e); + } + }); } } diff --git a/src/main/java/mp/code/intellij/ui/CodeMPToolWindow.java b/src/main/java/mp/code/intellij/ui/CodeMPToolWindow.java new file mode 100644 index 0000000..ebe9f69 --- /dev/null +++ b/src/main/java/mp/code/intellij/ui/CodeMPToolWindow.java @@ -0,0 +1,119 @@ +package mp.code.intellij.ui; + +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.ui.Messages; +import com.intellij.ui.treeStructure.Tree; +import mp.code.intellij.CodeMP; +import mp.code.intellij.util.FileUtil; +import mp.code.intellij.util.InteractionUtil; +import mp.code.intellij.util.cb.BufferCallback; + +import javax.swing.*; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.TreePath; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.MouseEvent; +import java.util.Optional; + +public class CodeMPToolWindow extends JPanel { + public CodeMPToolWindow(Project project) { + this.draw(project); + } + + public void redraw(Project project) { + this.draw(project); + this.repaint(); + } + + private void draw(Project project) { + this.removeAll(); + switch(CodeMPWindowFactory.getWindowState()) { + case DISCONNECTED -> { + JButton connectButton = new JButton(new AbstractAction("Connect...") { + @Override + public void actionPerformed(ActionEvent e) { + InteractionUtil.connect(project, () -> CodeMPToolWindow.this.redraw(project)); + } + }); + this.add(connectButton); + } + case CONNECTED -> { + this.setLayout(new GridLayout(0, 1)); + JTree tree = drawTree(InteractionUtil.listWorkspaces(project, true, true)); + tree.addMouseListener(new SimpleMouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + if(e.getClickCount() < 2) return; + TreePath path = tree.getPathForLocation(e.getX(), e.getY()); + if(path == null) return; + String workspaceName = path.getLastPathComponent().toString(); + InteractionUtil.joinWorkspace( + project, + workspaceName, + () -> CodeMPToolWindow.this.redraw(project) + ); + } + }); + this.add(tree); + } + case JOINED -> { + JButton createButton = new JButton(new AbstractAction("Create buffer") { + @Override + public void actionPerformed(ActionEvent e) { + String bufferPath = Messages.showInputDialog( + "Name of buffer:", + "CodeMP Buffer Create", + Messages.getQuestionIcon() + ); + + InteractionUtil.createBuffer(project, bufferPath); + CodeMPToolWindow.this.redraw(project); + } + }); + createButton.setSize(createButton.getPreferredSize()); + + JTree tree = drawTree(CodeMP.getActiveWorkspace().getFileTree(Optional.empty(), false)); + tree.addMouseListener(new SimpleMouseListener() { + @Override + public void mouseClicked(MouseEvent e) { + if(e.getClickCount() < 2) return; + TreePath path = tree.getPathForLocation(e.getX(), e.getY()); + if(path == null) return; + InteractionUtil.bufferAttach( + project, + CodeMP.getActiveWorkspace(), + path.getLastPathComponent().toString() + ).ifPresent(controller -> { + try { + Thread.sleep(1000); // TODO: this sucks + } catch(InterruptedException ignored) { + } + ApplicationManager.getApplication().runWriteAction(() -> { + try { + FileUtil.getAndRegisterBufferEquivalent(this, project, controller); + } catch(Exception ex) { + throw new RuntimeException(ex); + } + }); + controller.callback(buf -> new BufferCallback(project).accept(buf)); + }); + } + }); + + this.add(createButton); + this.add(tree); + } + } + } + + private JTree drawTree(String[] contents) { + DefaultMutableTreeNode root = new DefaultMutableTreeNode(); + for(String content : contents) { + root.add(new DefaultMutableTreeNode(content)); + } + + return new Tree(root); + } +} diff --git a/src/main/java/mp/code/intellij/ui/CodeMPWindowFactory.java b/src/main/java/mp/code/intellij/ui/CodeMPWindowFactory.java index 6570316..e30dd3b 100644 --- a/src/main/java/mp/code/intellij/ui/CodeMPWindowFactory.java +++ b/src/main/java/mp/code/intellij/ui/CodeMPWindowFactory.java @@ -1,29 +1,14 @@ package mp.code.intellij.ui; -import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.DumbAware; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Messages; import com.intellij.openapi.wm.ToolWindow; import com.intellij.openapi.wm.ToolWindowFactory; import com.intellij.ui.content.Content; import com.intellij.ui.content.ContentFactory; -import com.intellij.ui.treeStructure.Tree; -import com.jgoodies.forms.layout.FormLayout; import mp.code.intellij.CodeMP; -import mp.code.intellij.util.cb.BufferCallback; -import mp.code.intellij.util.FileUtil; -import mp.code.intellij.util.InteractionUtil; import org.jetbrains.annotations.NotNull; -import javax.swing.*; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.TreePath; -import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.MouseEvent; -import java.util.Optional; - public class CodeMPWindowFactory implements ToolWindowFactory, DumbAware { @Override public void createToolWindowContent( @@ -56,104 +41,4 @@ public class CodeMPWindowFactory implements ToolWindowFactory, DumbAware { CONNECTED, JOINED } - - public static class CodeMPToolWindow extends JPanel { - public CodeMPToolWindow(Project project) { - this.draw(project); - } - - private void redraw(Project project) { - this.draw(project); - this.repaint(); - } - - private void draw(Project project) { - this.removeAll(); - switch(getWindowState()) { - case DISCONNECTED -> { - JButton connectButton = new JButton(new AbstractAction("Connect...") { - @Override - public void actionPerformed(ActionEvent e) { - InteractionUtil.connect(project, () -> CodeMPToolWindow.this.redraw(project)); - } - }); - this.add(connectButton); - } - case CONNECTED -> { - this.setLayout(new GridLayout(0, 1)); - JTree tree = drawTree(InteractionUtil.listWorkspaces(project, true, true)); - tree.addMouseListener(new SimpleMouseListener() { - @Override - public void mouseClicked(MouseEvent e) { - if(e.getClickCount() < 2) return; - TreePath path = tree.getPathForLocation(e.getX(), e.getY()); - if(path == null) return; - String workspaceName = path.getLastPathComponent().toString(); - InteractionUtil.joinWorkspace( - project, - workspaceName, - () -> CodeMPToolWindow.this.redraw(project) - ); - } - }); - this.add(tree); - } - case JOINED -> { - JButton createButton = new JButton(new AbstractAction("Create buffer") { - @Override - public void actionPerformed(ActionEvent e) { - String bufferPath = Messages.showInputDialog( - "Name of buffer:", - "CodeMP Buffer Create", - Messages.getQuestionIcon() - ); - - InteractionUtil.bufferCreate(project, bufferPath); - CodeMPToolWindow.this.redraw(project); - } - }); - createButton.setSize(createButton.getPreferredSize()); - - JTree tree = drawTree(CodeMP.getActiveWorkspace().getFileTree(Optional.empty(), false)); - tree.addMouseListener(new SimpleMouseListener() { - @Override - public void mouseClicked(MouseEvent e) { - if(e.getClickCount() < 2) return; - TreePath path = tree.getPathForLocation(e.getX(), e.getY()); - if(path == null) return; - InteractionUtil.bufferAttach( - project, - CodeMP.getActiveWorkspace(), - path.getLastPathComponent().toString() - ).ifPresent(controller -> { - try { - Thread.sleep(1000); // TODO: this sucks - } catch(InterruptedException ignored) {} - ApplicationManager.getApplication().runWriteAction(() -> { - try { - FileUtil.getAndRegisterBufferEquivalent(this, project, controller); - } catch(Exception ex) { - throw new RuntimeException(ex); - } - }); - controller.callback(buf -> new BufferCallback(project).accept(buf)); - }); - } - }); - - this.add(createButton); - this.add(tree); - } - } - } - - private JTree drawTree(String[] contents) { - DefaultMutableTreeNode root = new DefaultMutableTreeNode(); - for(String content : contents) { - root.add(new DefaultMutableTreeNode(content)); - } - - return new Tree(root); - } - } } diff --git a/src/main/java/mp/code/intellij/util/InteractionUtil.java b/src/main/java/mp/code/intellij/util/InteractionUtil.java index e834aeb..9e2d287 100644 --- a/src/main/java/mp/code/intellij/util/InteractionUtil.java +++ b/src/main/java/mp/code/intellij/util/InteractionUtil.java @@ -11,6 +11,7 @@ import com.intellij.openapi.progress.ProgressIndicator; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.Task; import com.intellij.openapi.project.Project; +import com.intellij.openapi.wm.ToolWindowManager; import mp.code.BufferController; import mp.code.Client; import mp.code.Workspace; @@ -20,6 +21,7 @@ import mp.code.intellij.CodeMP; import mp.code.intellij.listeners.BufferEventListener; import mp.code.intellij.listeners.CursorEventListener; import mp.code.intellij.settings.CodeMPSettings; +import mp.code.intellij.ui.CodeMPToolWindow; import mp.code.intellij.util.cb.CursorCallback; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -33,7 +35,7 @@ import java.util.Optional; * like notifications and error handling. */ public class InteractionUtil { - public static void connect(@Nullable Project project, @Nullable Runnable after) { + public static void connect(@NotNull Project project, @Nullable Runnable after) { ProgressManager.getInstance().run(new Task.Backgroundable(project, "Connecting to CodeMP server...") { @Override public void run(@NotNull ProgressIndicator indicator) { @@ -48,6 +50,7 @@ public class InteractionUtil { if(after != null) after.run(); + refreshToolWindow(project); notifyInfo(project, "Success", "Connected to server!"); } catch(NullPointerException e) { Notifications.Bus.notify(new Notification( @@ -63,29 +66,21 @@ public class InteractionUtil { }); } - public static void disconnect(@Nullable Project project) { + public static void disconnect(@NotNull Project project) { CodeMP.disconnect(); MemoryManager.endClientLifetime(); + refreshToolWindow(project); notifyInfo(project, "Success", "Disconnected from server!"); } - public static void createWorkspace(Project project, @NotNull String workspaceId, @Nullable Runnable after) { + public static void createWorkspace(@NotNull Project project, @NotNull String workspaceId, @Nullable Runnable after) { ProgressManager.getInstance().run(new Task.Backgroundable(project, String.format("Creating workspace %s...", workspaceId)) { @Override public void run(@NotNull ProgressIndicator indicator) { - if(project == null) { - Notifications.Bus.notify(new Notification( - "CodeMP", - "No project found", - "Please ensure that you have an open project before attempting to create a workspace.", - NotificationType.ERROR - ), null); - return; - } - try { CodeMP.getClient("workspace create").createWorkspace(workspaceId); if(after != null) after.run(); + refreshToolWindow(project); notifyInfo( project, "Success", @@ -101,27 +96,18 @@ public class InteractionUtil { }); } - public static void inviteToWorkspace(Project project, @NotNull String workspaceId, @NotNull String userName, @Nullable Runnable after) { + public static void inviteToWorkspace(@NotNull Project project, @NotNull String workspaceId, @NotNull String userName, @Nullable Runnable after) { ProgressManager.getInstance().run(new Task.Backgroundable(project, String.format("Inviting %s to workspace %s...", userName, workspaceId)) { @Override public void run(@NotNull ProgressIndicator indicator) { - if(project == null) { - Notifications.Bus.notify(new Notification( - "CodeMP", - "No project found", - "Please ensure that you have an open project before attempting to join a workspace.", - NotificationType.ERROR - ), null); - return; - } - try { CodeMP.getClient("workspace invite").inviteToWorkspace(workspaceId, userName); if(after != null) after.run(); + refreshToolWindow(project); notifyInfo( project, "Success", - String.format("Joined workspace %s!", workspaceId) + String.format("Invited %s to workspace %s!", userName, workspaceId) ); } catch(ConnectionException e) { InteractionUtil.notifyError(project, String.format( @@ -133,23 +119,14 @@ public class InteractionUtil { }); } - public static void joinWorkspace(Project project, @NotNull String workspaceId, @Nullable Runnable after) { + public static void joinWorkspace(@NotNull Project project, @NotNull String workspaceId, @Nullable Runnable after) { ProgressManager.getInstance().run(new Task.Backgroundable(project, String.format("Joining workspace %s...", workspaceId)) { @Override public void run(@NotNull ProgressIndicator indicator) { - if(project == null) { - Notifications.Bus.notify(new Notification( - "CodeMP", - "No project found", - "Please ensure that you have an open project before attempting to join a workspace.", - NotificationType.ERROR - ), null); - return; - } - try { CodeMP.joinWorkspace(workspaceId); MemoryManager.startWorkspaceLifetime(workspaceId); + refreshToolWindow(project); } catch(ConnectionException e) { InteractionUtil.notifyError(project, String.format( "Failed to join workspace %s!", @@ -181,20 +158,10 @@ public class InteractionUtil { }); } - public static void deleteWorkspace(Project project, @NotNull String workspaceId, @Nullable Runnable after) { + public static void deleteWorkspace(@NotNull Project project, @NotNull String workspaceId, @Nullable Runnable after) { ProgressManager.getInstance().run(new Task.Backgroundable(project, String.format("Deleting workspace %s...", workspaceId)) { @Override public void run(@NotNull ProgressIndicator indicator) { - if(project == null) { - Notifications.Bus.notify(new Notification( - "CodeMP", - "No project found", - "Please ensure that you have an open project before attempting to delete a workspace.", - NotificationType.ERROR - ), null); - return; - } - try { Client client = CodeMP.getClient("workspace delete"); client.deleteWorkspace(workspaceId); @@ -202,11 +169,13 @@ public class InteractionUtil { Optional ws = client.getWorkspace("workspace leave"); if(ws.isPresent() && ws.get().getWorkspaceId().equals(workspaceId)) { CodeMP.leaveWorkspace(); - MemoryManager.startWorkspaceLifetime(workspaceId); + MemoryManager.endWorkspaceLifetime(workspaceId); } if(after != null) after.run(); + refreshToolWindow(project); + notifyInfo( project, "Success", @@ -222,14 +191,21 @@ public class InteractionUtil { }); } - public static void leaveWorkspace(Project project, String workspaceId) { - CodeMP.leaveWorkspace(); - MemoryManager.endWorkspaceLifetime(workspaceId); - notifyInfo( - project, - "Success", - String.format("Left workspace %s!", workspaceId) - ); + public static void leaveWorkspace(@NotNull Project project, @NotNull String workspaceId, @Nullable Runnable after) { + ProgressManager.getInstance().run(new Task.Backgroundable(project, String.format("Leaving workspace %s...", workspaceId)) { + @Override + public void run(@NotNull ProgressIndicator indicator) { + CodeMP.leaveWorkspace(); + MemoryManager.endWorkspaceLifetime(workspaceId); + if(after != null) after.run(); + refreshToolWindow(project); + notifyInfo( + project, + "Success", + String.format("Left workspace %s!", workspaceId) + ); + } + }); } public static String[] listWorkspaces(Project project, boolean owned, boolean invited) { @@ -258,10 +234,11 @@ public class InteractionUtil { } } - public static void bufferCreate(Project project, String path) { + public static void createBuffer(Project project, String path) { try { Workspace workspace = CodeMP.getActiveWorkspace(); workspace.createBuffer(path); + refreshToolWindow(project); } catch(ConnectionRemoteException e) { notifyError(project, "Failed to create a buffer!", e); } @@ -281,4 +258,10 @@ public class InteractionUtil { ), project); CodeMP.LOGGER.error(title, t); } + + public static void refreshToolWindow(Project project) { + CodeMPToolWindow w = (CodeMPToolWindow) ToolWindowManager.getInstance(project).getToolWindow("CodeMP"); + if(w == null) return; + w.redraw(project); + } } diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index ef9b658..7b9211b 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -10,19 +10,27 @@ - + - - - - - + + + + + + + + + + + +