diff --git a/plugin.xml b/plugin.xml
index 511077b..138bafa 100644
--- a/plugin.xml
+++ b/plugin.xml
@@ -97,5 +97,11 @@
name="Share Client">
+
+
+
+
diff --git a/src/main/java/me/mrletsplay/shareclient/ShareClient.java b/src/main/java/me/mrletsplay/shareclient/ShareClient.java
index 0efa03d..05f3a1c 100644
--- a/src/main/java/me/mrletsplay/shareclient/ShareClient.java
+++ b/src/main/java/me/mrletsplay/shareclient/ShareClient.java
@@ -4,6 +4,7 @@ import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
@@ -20,26 +21,38 @@ import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IStartup;
+import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.BundleContext;
+import me.mrletsplay.shareclient.util.ChecksumUtil;
import me.mrletsplay.shareclient.util.Peer;
import me.mrletsplay.shareclient.util.ProjectRelativePath;
import me.mrletsplay.shareclient.util.ShareSession;
+import me.mrletsplay.shareclient.util.listeners.ShareClientPageListener;
+import me.mrletsplay.shareclient.util.listeners.ShareClientPartListener;
+import me.mrletsplay.shareclient.util.listeners.ShareClientWindowListener;
import me.mrletsplay.shareclient.views.ShareView;
+import me.mrletsplay.shareclientcore.connection.Change;
import me.mrletsplay.shareclientcore.connection.ConnectionException;
import me.mrletsplay.shareclientcore.connection.MessageListener;
import me.mrletsplay.shareclientcore.connection.RemoteConnection;
import me.mrletsplay.shareclientcore.connection.WebSocketConnection;
+import me.mrletsplay.shareclientcore.connection.message.AddressableMessage;
+import me.mrletsplay.shareclientcore.connection.message.ChangeMessage;
+import me.mrletsplay.shareclientcore.connection.message.ChecksumMessage;
import me.mrletsplay.shareclientcore.connection.message.FullSyncMessage;
import me.mrletsplay.shareclientcore.connection.message.Message;
import me.mrletsplay.shareclientcore.connection.message.PeerJoinMessage;
+import me.mrletsplay.shareclientcore.connection.message.PeerLeaveMessage;
import me.mrletsplay.shareclientcore.connection.message.RequestFullSyncMessage;
+import me.mrletsplay.shareclientcore.document.SharedDocument;
/**
* The activator class controls the plug-in life cycle
*/
-public class ShareClient extends AbstractUIPlugin implements MessageListener {
+public class ShareClient extends AbstractUIPlugin implements MessageListener, IStartup {
// The plug-in ID
public static final String PLUGIN_ID = "ShareClient"; //$NON-NLS-1$
@@ -47,11 +60,15 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener {
// The shared instance
private static ShareClient plugin;
+ private ShareClientPartListener partListener = new ShareClientPartListener();
+
private ShareView view;
private ShareSession activeSession;
- public ShareClient() {
+ private Map sharedDocuments;
+ public ShareClient() {
+ this.sharedDocuments = new HashMap<>();
}
@Override
@@ -147,6 +164,11 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener {
updateView();
}
+ if(message instanceof PeerLeaveMessage leave) {
+ activeSession.getPeers().removeIf(p -> p.siteID() == leave.peerSiteID());
+ updateView();
+ }
+
if (message instanceof FullSyncMessage sync) {
// TODO: handle FULL_SYNC
ProjectRelativePath path;
@@ -182,6 +204,8 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener {
Files.createFile(filePath);
}
+ // TODO: update sharedDocuments
+
Files.write(filePath, sync.content());
project.refreshLocal(IResource.DEPTH_INFINITE, null);
} catch (IOException | CoreException e) {
@@ -227,18 +251,50 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener {
RemoteConnection connection = activeSession.getConnection();
for (var en : paths.entrySet()) {
- if (!Files.isRegularFile(en.getValue()))
- continue;
- try {
- byte[] bytes = Files.readAllBytes(en.getValue());
- connection.send(new FullSyncMessage(req.siteID(), en.getKey().toString(), bytes));
- } catch (IOException | ConnectionException e) {
- e.printStackTrace();
- MessageDialog.openError(null, "Share Client", "Failed to send file contents: " + e.toString());
- return;
- }
+ if(!sendFullSyncOrChecksum(connection, req.siteID(), en.getKey(), en.getValue(), false)) return;
}
}
+
+ if(message instanceof ChangeMessage change) {
+ Change c = change.change();
+ try {
+ ProjectRelativePath path = ProjectRelativePath.of(c.documentPath());
+ }catch(IllegalArgumentException e) {
+ return;
+ }
+
+ // TODO: insert change into document in sharedDocuments
+ }
+ }
+
+ public void addSharedProject(IProject project) {
+ activeSession.getSharedProjects().add(project);
+
+ RemoteConnection connection = activeSession.getConnection();
+ for(Map.Entry en : getProjectFiles(project).entrySet()) {
+ // TODO: add new document to sharedDocuments
+ if(!sendFullSyncOrChecksum(connection, AddressableMessage.BROADCAST_SITE_ID, en.getKey(), en.getValue(), false)) return;
+ }
+ }
+
+ private boolean sendFullSyncOrChecksum(RemoteConnection connection, int siteID, ProjectRelativePath relativePath, Path filePath, boolean checksum) {
+ if (!Files.isRegularFile(filePath)) return false;
+
+ try {
+ byte[] bytes = Files.readAllBytes(filePath);
+
+ if(!checksum) {
+ connection.send(new FullSyncMessage(siteID, relativePath.toString(), bytes));
+ }else {
+ connection.send(new ChecksumMessage(siteID, relativePath.toString(), ChecksumUtil.generateSHA256(bytes)));
+ }
+ } catch (IOException | ConnectionException e) {
+ e.printStackTrace();
+ MessageDialog.openError(null, "Share Client", "Failed to send file contents: " + e.toString());
+ return false;
+ }
+
+ return true;
}
private Map getProjectFiles(IProject project) {
@@ -255,4 +311,20 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener {
}
}
+ public ShareClientPartListener getPartListener() {
+ return partListener;
+ }
+
+ @Override
+ public void earlyStartup() {
+ PlatformUI.getWorkbench().addWindowListener(ShareClientWindowListener.INSTANCE);
+ Arrays.stream(PlatformUI.getWorkbench().getWorkbenchWindows()).forEach(w -> {
+ w.addPageListener(ShareClientPageListener.INSTANCE);
+ Arrays.stream(w.getPages()).forEach(p -> {
+ p.addPartListener(partListener);
+ Arrays.stream(p.getEditorReferences()).forEach(e -> partListener.addDocumentListener(e));
+ });
+ });
+ }
+
}
diff --git a/src/main/java/me/mrletsplay/shareclient/handlers/ShareProjectHandler.java b/src/main/java/me/mrletsplay/shareclient/handlers/ShareProjectHandler.java
index dcfc9a9..8b98a6d 100644
--- a/src/main/java/me/mrletsplay/shareclient/handlers/ShareProjectHandler.java
+++ b/src/main/java/me/mrletsplay/shareclient/handlers/ShareProjectHandler.java
@@ -35,9 +35,7 @@ public class ShareProjectHandler extends AbstractHandler {
ShareSession session = ShareClient.getDefault().getOrStartSession();
if(session == null) return null;
- session.getSharedProjects().add(project);
-
- // TODO: broadcast FULL_SYNC for newly added project
+ ShareClient.getDefault().addSharedProject(project);
// IEditorPart editor = window.getActivePage().getActiveEditor();
// if(!(editor instanceof ITextEditor)) return null;
diff --git a/src/main/java/me/mrletsplay/shareclient/util/ChecksumUtil.java b/src/main/java/me/mrletsplay/shareclient/util/ChecksumUtil.java
new file mode 100644
index 0000000..49da555
--- /dev/null
+++ b/src/main/java/me/mrletsplay/shareclient/util/ChecksumUtil.java
@@ -0,0 +1,22 @@
+package me.mrletsplay.shareclient.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class ChecksumUtil {
+
+ private static final MessageDigest SHA_256;
+
+ static {
+ try {
+ SHA_256 = MessageDigest.getInstance("SHA-256");
+ }catch(NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public static byte[] generateSHA256(byte[] bytes) {
+ return SHA_256.digest(bytes);
+ }
+
+}
diff --git a/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientPageListener.java b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientPageListener.java
new file mode 100644
index 0000000..6619071
--- /dev/null
+++ b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientPageListener.java
@@ -0,0 +1,27 @@
+package me.mrletsplay.shareclient.util.listeners;
+
+import org.eclipse.ui.IPageListener;
+import org.eclipse.ui.IWorkbenchPage;
+
+import me.mrletsplay.shareclient.ShareClient;
+
+public class ShareClientPageListener implements IPageListener {
+
+ public static final ShareClientPageListener INSTANCE = new ShareClientPageListener();
+
+ private ShareClientPageListener() {}
+
+ @Override
+ public void pageOpened(IWorkbenchPage page) {
+ page.addPartListener(ShareClient.getDefault().getPartListener());
+ }
+
+ @Override
+ public void pageClosed(IWorkbenchPage page) {
+ page.removePartListener(ShareClient.getDefault().getPartListener());
+ }
+
+ @Override
+ public void pageActivated(IWorkbenchPage page) {}
+
+}
diff --git a/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientPartListener.java b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientPartListener.java
new file mode 100644
index 0000000..f97aaa9
--- /dev/null
+++ b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientPartListener.java
@@ -0,0 +1,77 @@
+package me.mrletsplay.shareclient.util.listeners;
+
+import java.nio.file.Path;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.jface.text.DocumentEvent;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IDocumentListener;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IPartListener2;
+import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.IWorkbenchPartReference;
+import org.eclipse.ui.part.FileEditorInput;
+import org.eclipse.ui.texteditor.ITextEditor;
+
+import me.mrletsplay.shareclient.util.ProjectRelativePath;
+
+public class ShareClientPartListener implements IPartListener2 {
+
+ private Map listeners = new HashMap<>();
+
+ private IDocumentListener createListener(ProjectRelativePath path) {
+ if(listeners.containsKey(path)) return listeners.get(path);
+
+ IDocumentListener listener = new IDocumentListener() {
+
+ private boolean ignoreChanges = false;
+
+ @Override
+ public void documentChanged(DocumentEvent event) {
+ System.out.println("Change in document at " + path);
+ }
+
+ @Override
+ public void documentAboutToBeChanged(DocumentEvent event) {
+
+ }
+ };
+ listeners.put(path, listener);
+ return listener;
+ }
+
+ public void addDocumentListener(IWorkbenchPartReference partRef) {
+ IWorkbenchPart part = partRef.getPart(false);
+ if(!(part instanceof IEditorPart)) return;
+ IEditorPart editor = (IEditorPart) part;
+ if(!(editor instanceof ITextEditor)) return;
+ ITextEditor textEditor = (ITextEditor) editor;
+ IEditorInput editorInput = editor.getEditorInput();
+ if(!(editorInput instanceof FileEditorInput)) return;
+ FileEditorInput fileEditorInput = (FileEditorInput) editorInput;
+ IDocument document = textEditor.getDocumentProvider().getDocument(editorInput);
+
+ IFile file = fileEditorInput.getFile();
+ IProject project = file.getProject();
+
+ Path filePath = project.getLocation().toPath().relativize(file.getLocation().toPath());
+ ProjectRelativePath relPath = new ProjectRelativePath(project.getName(), filePath.toString());
+ System.out.println(relPath);
+ document.addDocumentListener(createListener(relPath));
+ }
+
+ @Override
+ public void partOpened(IWorkbenchPartReference partRef) {
+ addDocumentListener(partRef);
+ }
+
+ @Override
+ public void partInputChanged(IWorkbenchPartReference partRef) {
+ addDocumentListener(partRef);
+ }
+
+}
diff --git a/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientWindowListener.java b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientWindowListener.java
new file mode 100644
index 0000000..228403c
--- /dev/null
+++ b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientWindowListener.java
@@ -0,0 +1,28 @@
+package me.mrletsplay.shareclient.util.listeners;
+
+import org.eclipse.ui.IWindowListener;
+import org.eclipse.ui.IWorkbenchWindow;
+
+public class ShareClientWindowListener implements IWindowListener {
+
+ public static final ShareClientWindowListener INSTANCE = new ShareClientWindowListener();
+
+ private ShareClientWindowListener() {}
+
+ @Override
+ public void windowOpened(IWorkbenchWindow window) {
+ window.addPageListener(ShareClientPageListener.INSTANCE);
+ }
+
+ @Override
+ public void windowClosed(IWorkbenchWindow window) {
+ window.removePageListener(ShareClientPageListener.INSTANCE);
+ }
+
+ @Override
+ public void windowActivated(IWorkbenchWindow window) {}
+
+ @Override
+ public void windowDeactivated(IWorkbenchWindow window) {}
+
+}
diff --git a/src/main/java/me/mrletsplay/shareclient/views/ShareView.java b/src/main/java/me/mrletsplay/shareclient/views/ShareView.java
index 1ad49d5..e1df2e8 100644
--- a/src/main/java/me/mrletsplay/shareclient/views/ShareView.java
+++ b/src/main/java/me/mrletsplay/shareclient/views/ShareView.java
@@ -5,7 +5,6 @@ import java.util.Arrays;
import javax.inject.Inject;
-import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.dialogs.InputDialog;
@@ -105,8 +104,6 @@ public class ShareView extends ViewPart {
@Override
public void run() {
- showMessage(Arrays.stream(ResourcesPlugin.getWorkspace().getRoot().getProjects()).map(p -> p.getName() + ": " + p.getLocation().toString()).toList().toString());
-
InputDialog input = new InputDialog(viewer.getControl().getShell(), "Join session", "Enter session id", "EEE", null);
input.setBlockOnOpen(true);
if(input.open() != InputDialog.OK) return;