diff --git a/eclipse.target b/eclipse.target
new file mode 100644
index 0000000..9b32648
--- /dev/null
+++ b/eclipse.target
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+ me.mrletsplay
+ ShareLib
+ 1.0-SNAPSHOT
+ jar
+
+
+
+
+
+
+ org.java-websocket
+ Java-WebSocket
+ 1.5.6
+ jar
+
+
+
+
+
+ x86_64
+ linux
+ gtk
+ en_US
+
+
+ -Declipse.p2.max.threads=10 -Doomph.update.url=https://download.eclipse.org/oomph/updates/milestone/latest -Doomph.redirection.index.redirection=index:/->http://git.eclipse.org/c/oomph/org.eclipse.oomph.git/plain/setups/ -Dosgi.requiredJavaVersion=17 -Dosgi.instance.area.default=@user.home/eclipse-workspace -Dosgi.dataAreaRequiresExplicitInit=true -Dorg.eclipse.swt.graphics.Resource.reportNonDisposed=true -Dsun.java.command=Eclipse -XX:+UseG1GC -XX:+UseStringDeduplication --add-modules=ALL-SYSTEM -Dorg.eclipse.ecf.provider.filetransfer.excludeContributors=org.eclipse.ecf.provider.filetransfer.httpclientjava -Dosgi.requiredJavaVersion=17 -Dosgi.instance.area.default=@user.home/eclipse-workspace -Dosgi.dataAreaRequiresExplicitInit=true -Dorg.eclipse.swt.graphics.Resource.reportNonDisposed=true -Declipse.e4.inject.javax.warning=false -Dorg.slf4j.simpleLogger.defaultLogLevel=off -Dsun.java.command=Eclipse -Xms256m -Xmx2048m -XX:+UseG1GC -XX:+UseStringDeduplication --add-modules=ALL-SYSTEM -Djava.security.manager=allow
+
+
\ No newline at end of file
diff --git a/src/main/java/me/mrletsplay/shareclient/ShareClient.java b/src/main/java/me/mrletsplay/shareclient/ShareClient.java
index cfb2d39..d3031de 100644
--- a/src/main/java/me/mrletsplay/shareclient/ShareClient.java
+++ b/src/main/java/me/mrletsplay/shareclient/ShareClient.java
@@ -30,6 +30,7 @@ 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.SharedProject;
import me.mrletsplay.shareclient.util.listeners.ShareClientDocumentListener;
import me.mrletsplay.shareclient.util.listeners.ShareClientPageListener;
import me.mrletsplay.shareclient.util.listeners.ShareClientPartListener;
@@ -37,6 +38,7 @@ 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.DisconnectListener;
import me.mrletsplay.shareclientcore.connection.MessageListener;
import me.mrletsplay.shareclientcore.connection.RemoteConnection;
import me.mrletsplay.shareclientcore.connection.WebSocketConnection;
@@ -53,7 +55,7 @@ import me.mrletsplay.shareclientcore.document.SharedDocument;
/**
* The activator class controls the plug-in life cycle
*/
-public class ShareClient extends AbstractUIPlugin implements MessageListener, IStartup {
+public class ShareClient extends AbstractUIPlugin implements MessageListener, DisconnectListener, IStartup {
// The plug-in ID
public static final String PLUGIN_ID = "ShareClient"; //$NON-NLS-1$
@@ -132,6 +134,7 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, IS
}
connection.addListener(this);
+ connection.setDisconnectListener(this);
updateView();
return activeSession = new ShareSession(connection, sessionID);
@@ -180,7 +183,6 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, IS
}
if (message instanceof FullSyncMessage sync) {
- // TODO: handle FULL_SYNC
ProjectRelativePath path;
try {
path = ProjectRelativePath.of(sync.documentPath());
@@ -189,27 +191,36 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, IS
return;
}
- ShareClientDocumentListener listener = partListener.getListeners().get(path);
+ ShareClientDocumentListener listener = partListener.getListener(path);
IWorkspace workspace = ResourcesPlugin.getWorkspace();
IWorkspaceRoot workspaceRoot = workspace.getRoot();
- IProject project = workspaceRoot.getProject(path.projectName());
- if(project == null) return;
- // TODO: make sure to not overwrite existing non-shared projects
- if (!project.exists()) {
- IProjectDescription description = workspace.newProjectDescription(path.projectName());
- try {
- project.create(description, null);
- project.open(null);
- } catch (CoreException e) {
- e.printStackTrace();
- MessageDialog.openError(null, "Share Client", "Failed to create project: " + e.toString());
- return;
+ SharedProject sharedProject = activeSession.getSharedProject(path.projectName());
+ if(sharedProject == null) {
+ // New project, TODO: prompt user to choose location
+ IProject project = workspaceRoot.getProject(path.projectName());
+ if(project == null) return;
+
+ // TODO: make sure to not overwrite existing non-shared projects
+ if (!project.exists()) {
+ IProjectDescription description = workspace.newProjectDescription(path.projectName());
+ try {
+ project.create(description, null);
+ project.open(null);
+ } catch (CoreException e) {
+ e.printStackTrace();
+ MessageDialog.openError(null, "Share Client", "Failed to create project: " + e.toString());
+ return;
+ }
+ System.out.println("Created project: " + project);
}
- System.out.println("Created project " + project);
+
+ sharedProject = new SharedProject(path.projectName(), project);
+ activeSession.getSharedProjects().add(sharedProject);
+ System.out.println("Received new project: " + path.projectName());
}
- Path filePath = project.getLocation().toPath().resolve(path.relativePath());
+ Path filePath = sharedProject.getLocal().getLocation().toPath().resolve(path.relativePath());
try {
if (!Files.exists(filePath)) {
Files.createDirectories(filePath.getParent());
@@ -218,10 +229,10 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, IS
// TODO: update sharedDocuments
- listener.setIgnoreChanges(true);
+ if(listener != null) listener.setIgnoreChanges(true);
Files.write(filePath, sync.content());
- project.refreshLocal(IResource.DEPTH_INFINITE, null);
- listener.setIgnoreChanges(false);
+ sharedProject.getLocal().refreshLocal(IResource.DEPTH_INFINITE, null);
+ if(listener != null) listener.setIgnoreChanges(false);
} catch (IOException | CoreException e) {
e.printStackTrace();
MessageDialog.openError(null, "Share Client", "Failed to update file: " + e.toString());
@@ -233,7 +244,7 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, IS
Map paths = new HashMap<>();
if (req.documentPath() == null) {
// Sync entire (shared) workspace
- for (IProject project : activeSession.getSharedProjects()) {
+ for (SharedProject project : activeSession.getSharedProjects()) {
var files = getProjectFiles(project);
System.out.println(files);
if (files == null) return;
@@ -247,18 +258,17 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, IS
return;
}
- IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(path.projectName());
- if (project == null || !project.exists()) return;
- if (!activeSession.getSharedProjects().contains(project)) return;
+ SharedProject sharedProject = activeSession.getSharedProject(path.projectName());
+ if (sharedProject == null) return;
if (!path.relativePath().isEmpty()) {
- Path projectLocation = project.getLocation().toPath();
+ Path projectLocation = sharedProject.getLocal().getLocation().toPath();
Path filePath = projectLocation.resolve(path.relativePath()).normalize();
if (!filePath.startsWith(projectLocation)) return;
- paths.put(new ProjectRelativePath(project.getName(), path.relativePath()), filePath);
+ paths.put(new ProjectRelativePath(sharedProject.getRemoteName(), path.relativePath()), filePath);
} else {
// Sync entire project
- var files = getProjectFiles(project);
+ var files = getProjectFiles(sharedProject);
if (files == null) return;
paths.putAll(files);
}
@@ -296,11 +306,21 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, IS
});
}
+ @Override
+ public void onDisconnect(String reason, boolean remote) {
+ MessageDialog.openError(null, "Share Client", "Disconnected (remote = " + remote + "): " + reason);
+ }
+
+ /**
+ * Shares a project. Includes sending full syncs to other clients
+ * @param project The project to share
+ */
public void addSharedProject(IProject project) {
- activeSession.getSharedProjects().add(project);
+ SharedProject shared = new SharedProject(activeSession.getFreeRemoteName(project.getName()), project);
+ activeSession.getSharedProjects().add(shared);
RemoteConnection connection = activeSession.getConnection();
- for(Map.Entry en : getProjectFiles(project).entrySet()) {
+ for(Map.Entry en : getProjectFiles(shared).entrySet()) {
// TODO: add new document to sharedDocuments
if(!sendFullSyncOrChecksum(connection, AddressableMessage.BROADCAST_SITE_ID, en.getKey(), en.getValue(), false)) return;
}
@@ -327,21 +347,20 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, IS
}
private Path resolvePath(ProjectRelativePath path) {
- IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(path.projectName());
- if (project == null || !project.exists()) return null;
- if (!activeSession.getSharedProjects().contains(project)) return null;
- Path projectLocation = project.getLocation().toPath();
+ SharedProject sharedProject = activeSession.getSharedProject(path.projectName());
+ if(sharedProject == null) return null;
+ Path projectLocation = sharedProject.getLocal().getLocation().toPath();
Path filePath = projectLocation.resolve(path.relativePath()).normalize();
if (!filePath.startsWith(projectLocation)) return null;
return filePath;
}
- private Map getProjectFiles(IProject project) {
+ private Map getProjectFiles(SharedProject project) {
try {
- Path projectLocation = project.getLocation().toPath();
+ Path projectLocation = project.getLocal().getLocation().toPath();
return Files.walk(projectLocation)
.collect(Collectors.toMap(
- p -> new ProjectRelativePath(project.getName(), projectLocation.relativize(p).toString()),
+ p -> new ProjectRelativePath(project.getRemoteName(), projectLocation.relativize(p).toString()),
Function.identity()));
} catch (IOException e) {
e.printStackTrace();
diff --git a/src/main/java/me/mrletsplay/shareclient/util/ProjectAndPath.java b/src/main/java/me/mrletsplay/shareclient/util/ProjectAndPath.java
new file mode 100644
index 0000000..2233a46
--- /dev/null
+++ b/src/main/java/me/mrletsplay/shareclient/util/ProjectAndPath.java
@@ -0,0 +1,5 @@
+package me.mrletsplay.shareclient.util;
+
+import org.eclipse.core.resources.IProject;
+
+public record ProjectAndPath(IProject project, String path) {}
\ No newline at end of file
diff --git a/src/main/java/me/mrletsplay/shareclient/util/ShareSession.java b/src/main/java/me/mrletsplay/shareclient/util/ShareSession.java
index e6dbca1..7ef0c7f 100644
--- a/src/main/java/me/mrletsplay/shareclient/util/ShareSession.java
+++ b/src/main/java/me/mrletsplay/shareclient/util/ShareSession.java
@@ -22,7 +22,7 @@ public class ShareSession {
private RemoteConnection connection;
private String sessionID;
- private List sharedProjects;
+ private List sharedProjects;
private Map sharedDocuments;
private List peers;
@@ -42,10 +42,38 @@ public class ShareSession {
return sessionID;
}
- public List getSharedProjects() {
+ public List getSharedProjects() {
return sharedProjects;
}
+ public SharedProject getSharedProject(String remoteName) {
+ return sharedProjects.stream()
+ .filter(p -> p.getRemoteName().equals(remoteName))
+ .findFirst().orElse(null);
+ }
+
+ public SharedProject getSharedProject(IProject project) {
+ return sharedProjects.stream()
+ .filter(p -> p.getLocal().equals(project))
+ .findFirst().orElse(null);
+ }
+
+ /**
+ * @param projectName The name of the project
+ * @return A remote name for the project that is not currently in use
+ */
+ public String getFreeRemoteName(String projectName) {
+ // TODO: technically, this can fail if two people try to share a project with the same name at the same time
+ if(getSharedProject(projectName) == null) return projectName;
+
+ int n = 0;
+ while(getSharedProject(projectName + n) != null) {
+ n++;
+ }
+
+ return projectName + n;
+ }
+
public Map getSharedDocuments() {
return sharedDocuments;
}
@@ -63,8 +91,9 @@ public class ShareSession {
@Override
public void onInsert(int index, char character) {
+ // FIXME: potential desync. If changes arrive while waiting for asyncExec to execute, it will insert at the wrong position
Display.getDefault().asyncExec(() -> {
- ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListeners().get(path);
+ ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListener(path);
if(documentListener != null) {
IDocument document = documentListener.getDocument();
@@ -84,7 +113,7 @@ public class ShareSession {
@Override
public void onDelete(int index) {
Display.getDefault().asyncExec(() -> {
- ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListeners().get(path);
+ ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListener(path);
if(documentListener != null) {
IDocument document = documentListener.getDocument();
diff --git a/src/main/java/me/mrletsplay/shareclient/util/SharedProject.java b/src/main/java/me/mrletsplay/shareclient/util/SharedProject.java
new file mode 100644
index 0000000..97c9591
--- /dev/null
+++ b/src/main/java/me/mrletsplay/shareclient/util/SharedProject.java
@@ -0,0 +1,27 @@
+package me.mrletsplay.shareclient.util;
+
+import org.eclipse.core.resources.IProject;
+
+/**
+ * A project shared in a session. Contains a remote name (name used for {@link ProjectRelativePath}s) and a reference to the local project.
+ */
+public class SharedProject {
+
+ private String remoteName;
+ private IProject local;
+
+ public SharedProject(String remoteName, IProject local) {
+ super();
+ this.remoteName = remoteName;
+ this.local = local;
+ }
+
+ public String getRemoteName() {
+ return remoteName;
+ }
+
+ public IProject getLocal() {
+ return local;
+ }
+
+}
diff --git a/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientDocumentListener.java b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientDocumentListener.java
index ccbd069..0b567af 100644
--- a/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientDocumentListener.java
+++ b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientDocumentListener.java
@@ -6,18 +6,20 @@ import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.swt.widgets.Display;
import me.mrletsplay.shareclient.ShareClient;
+import me.mrletsplay.shareclient.util.ProjectAndPath;
import me.mrletsplay.shareclient.util.ProjectRelativePath;
import me.mrletsplay.shareclient.util.ShareSession;
+import me.mrletsplay.shareclient.util.SharedProject;
import me.mrletsplay.shareclientcore.document.SharedDocument;
public class ShareClientDocumentListener implements IDocumentListener {
- private ProjectRelativePath path;
+ private ProjectAndPath path;
private IDocument document;
private boolean ignoreChanges = false;
- public ShareClientDocumentListener(ProjectRelativePath path, IDocument document) {
+ public ShareClientDocumentListener(ProjectAndPath path, IDocument document) {
this.path = path;
this.document = document;
}
@@ -40,7 +42,10 @@ public class ShareClientDocumentListener implements IDocumentListener {
ShareSession session = ShareClient.getDefault().getActiveSession();
if(session == null) return;
- SharedDocument doc = session.getOrCreateSharedDocument(path, () -> event.fDocument.get());
+ SharedProject sharedProject = session.getSharedProject(path.project());
+ if(sharedProject == null) return;
+
+ SharedDocument doc = session.getOrCreateSharedDocument(new ProjectRelativePath(sharedProject.getRemoteName(), path.path()), () -> event.fDocument.get());
if(event.getLength() > 0) {
doc.localDelete(event.getOffset(), event.getLength());
}
diff --git a/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientPartListener.java b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientPartListener.java
index 0c76057..c77b4a1 100644
--- a/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientPartListener.java
+++ b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientPartListener.java
@@ -15,13 +15,17 @@ import org.eclipse.ui.IWorkbenchPartReference;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
+import me.mrletsplay.shareclient.ShareClient;
+import me.mrletsplay.shareclient.util.ProjectAndPath;
import me.mrletsplay.shareclient.util.ProjectRelativePath;
+import me.mrletsplay.shareclient.util.ShareSession;
+import me.mrletsplay.shareclient.util.SharedProject;
public class ShareClientPartListener implements IPartListener2 {
- private Map listeners = new HashMap<>();
+ private Map listeners = new HashMap<>();
- private ShareClientDocumentListener createListener(ProjectRelativePath path, IDocument document) {
+ private ShareClientDocumentListener createListener(ProjectAndPath path, IDocument document) {
if(listeners.containsKey(path)) return listeners.get(path);
ShareClientDocumentListener listener = new ShareClientDocumentListener(path, document);
listeners.put(path, listener);
@@ -43,7 +47,8 @@ public class ShareClientPartListener implements IPartListener2 {
IProject project = file.getProject();
Path filePath = project.getLocation().toPath().relativize(file.getLocation().toPath());
- ProjectRelativePath relPath = new ProjectRelativePath(project.getName(), filePath.toString());
+ // FIXME: this should probably just store an IProject with a relative path
+ ProjectAndPath relPath = new ProjectAndPath(project, filePath.toString());
System.out.println("Opened editor: " + relPath);
document.addDocumentListener(createListener(relPath, document));
}
@@ -58,8 +63,18 @@ public class ShareClientPartListener implements IPartListener2 {
addDocumentListener(partRef);
}
- public Map getListeners() {
+ public Map getListeners() {
return listeners;
}
+ public ShareClientDocumentListener getListener(ProjectRelativePath path) {
+ ShareSession session = ShareClient.getDefault().getActiveSession();
+ if(session == null) return null;
+
+ SharedProject project = session.getSharedProject(path.projectName());
+ if(project == null) return null;
+
+ return listeners.get(new ProjectAndPath(project.getLocal(), path.relativePath()));
+ }
+
}
diff --git a/src/main/java/me/mrletsplay/shareclient/views/ShareView.java b/src/main/java/me/mrletsplay/shareclient/views/ShareView.java
index e1df2e8..5e884e5 100644
--- a/src/main/java/me/mrletsplay/shareclient/views/ShareView.java
+++ b/src/main/java/me/mrletsplay/shareclient/views/ShareView.java
@@ -133,6 +133,7 @@ public class ShareView extends ViewPart {
public void run() {
Clipboard clipboard = new Clipboard(Display.getDefault());
clipboard.setContents(new String[] {ShareClient.getDefault().getActiveSession().getSessionID()}, new Transfer[] {TextTransfer.getInstance()});
+ showMessage("The session ID has been copied to the clipboard");
}
};