Add debug dialog, Use bytes everywhere, Sync somewhat works (WIP)
This commit is contained in:
parent
d47edc9e1a
commit
09d7d80d29
BIN
icons/bug.png
Normal file
BIN
icons/bug.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 502 B |
BIN
icons/bug@2x.png
Normal file
BIN
icons/bug@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 945 B |
@ -2,7 +2,6 @@ package me.mrletsplay.shareclient;
|
|||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@ -29,6 +28,7 @@ import org.osgi.framework.BundleContext;
|
|||||||
|
|
||||||
import me.mrletsplay.shareclient.util.ChecksumUtil;
|
import me.mrletsplay.shareclient.util.ChecksumUtil;
|
||||||
import me.mrletsplay.shareclient.util.Peer;
|
import me.mrletsplay.shareclient.util.Peer;
|
||||||
|
import me.mrletsplay.shareclient.util.ProjectAndPath;
|
||||||
import me.mrletsplay.shareclient.util.ProjectRelativePath;
|
import me.mrletsplay.shareclient.util.ProjectRelativePath;
|
||||||
import me.mrletsplay.shareclient.util.ShareSession;
|
import me.mrletsplay.shareclient.util.ShareSession;
|
||||||
import me.mrletsplay.shareclient.util.SharedProject;
|
import me.mrletsplay.shareclient.util.SharedProject;
|
||||||
@ -51,6 +51,7 @@ import me.mrletsplay.shareclientcore.connection.message.Message;
|
|||||||
import me.mrletsplay.shareclientcore.connection.message.PeerJoinMessage;
|
import me.mrletsplay.shareclientcore.connection.message.PeerJoinMessage;
|
||||||
import me.mrletsplay.shareclientcore.connection.message.PeerLeaveMessage;
|
import me.mrletsplay.shareclientcore.connection.message.PeerLeaveMessage;
|
||||||
import me.mrletsplay.shareclientcore.connection.message.RequestFullSyncMessage;
|
import me.mrletsplay.shareclientcore.connection.message.RequestFullSyncMessage;
|
||||||
|
import me.mrletsplay.shareclientcore.document.Char;
|
||||||
import me.mrletsplay.shareclientcore.document.SharedDocument;
|
import me.mrletsplay.shareclientcore.document.SharedDocument;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -230,17 +231,15 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, Di
|
|||||||
|
|
||||||
byte[] bytes = new byte[sync.content().size()];
|
byte[] bytes = new byte[sync.content().size()];
|
||||||
for(int i = 0; i < bytes.length; i++) {
|
for(int i = 0; i < bytes.length; i++) {
|
||||||
|
bytes[i] = sync.content().get(i).value();
|
||||||
}
|
}
|
||||||
String content = sync.content().stream()
|
|
||||||
.map(c -> String.valueOf(c.value()))
|
|
||||||
.collect(Collectors.joining());
|
|
||||||
|
|
||||||
SharedDocument sharedDocument = activeSession.getOrCreateSharedDocument(path, () -> content);
|
SharedDocument sharedDocument = activeSession.getOrCreateSharedDocument(path, null /* because the content will be replaced afterwards anyway */);
|
||||||
// TODO: update sharedDocuments
|
sharedDocument.clear();
|
||||||
|
for(Char c : sync.content()) sharedDocument.getCharBag().add(c);
|
||||||
|
|
||||||
if(listener != null) listener.setIgnoreChanges(true);
|
if(listener != null) listener.setIgnoreChanges(true);
|
||||||
Files.write(filePath, content.getBytes(StandardCharsets.UTF_8)); // TODO: may cause problems with binary files? but they shouldn't be shared in the first place, so...
|
Files.write(filePath, bytes);
|
||||||
sharedProject.getLocal().refreshLocal(IResource.DEPTH_INFINITE, null);
|
sharedProject.getLocal().refreshLocal(IResource.DEPTH_INFINITE, null);
|
||||||
if(listener != null) listener.setIgnoreChanges(false);
|
if(listener != null) listener.setIgnoreChanges(false);
|
||||||
} catch (IOException | CoreException e) {
|
} catch (IOException | CoreException e) {
|
||||||
@ -299,17 +298,13 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, Di
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(activeSession.getSharedDocument(path) != null) return;
|
SharedDocument doc = activeSession.getSharedDocument(path); // Doesn't use getOrCreateSharedDocument, because it doesn't make sense to apply a change to a document that wasn't previously shared
|
||||||
|
|
||||||
// FIXME: doesn't work
|
if(doc == null) {
|
||||||
SharedDocument doc = activeSession.getOrCreateSharedDocument(path, () -> {
|
// TODO: show proper synchronization issue warning, because all received changes should be in previously shared documents
|
||||||
try {
|
MessageDialog.openError(null, "Share Client", "Synchronization issue (received change in non-shared document)");
|
||||||
return Files.readString(resolvePath(path));
|
return;
|
||||||
}catch(IOException e) {
|
}
|
||||||
MessageDialog.openError(null, "Share Client", "Failed to read file: " + e.toString());
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
doc.onMessage(message);
|
doc.onMessage(message);
|
||||||
}
|
}
|
||||||
@ -333,7 +328,7 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, Di
|
|||||||
for(Map.Entry<ProjectRelativePath, Path> en : getProjectFiles(shared).entrySet()) {
|
for(Map.Entry<ProjectRelativePath, Path> en : getProjectFiles(shared).entrySet()) {
|
||||||
activeSession.getOrCreateSharedDocument(en.getKey(), () -> {
|
activeSession.getOrCreateSharedDocument(en.getKey(), () -> {
|
||||||
try {
|
try {
|
||||||
return Files.readString(en.getValue(), StandardCharsets.UTF_8);
|
return Files.readAllBytes(en.getValue());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
@ -364,7 +359,13 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, Di
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Path resolvePath(ProjectRelativePath path) {
|
private ProjectAndPath resolveToProjectAndPath(ProjectRelativePath path) {
|
||||||
|
SharedProject sharedProject = activeSession.getSharedProject(path.projectName());
|
||||||
|
if(sharedProject == null) return null;
|
||||||
|
return new ProjectAndPath(sharedProject.getLocal(), path.relativePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Path resolveToPath(ProjectRelativePath path) {
|
||||||
SharedProject sharedProject = activeSession.getSharedProject(path.projectName());
|
SharedProject sharedProject = activeSession.getSharedProject(path.projectName());
|
||||||
if(sharedProject == null) return null;
|
if(sharedProject == null) return null;
|
||||||
Path projectLocation = sharedProject.getLocal().getLocation().toPath();
|
Path projectLocation = sharedProject.getLocal().getLocation().toPath();
|
||||||
|
@ -21,6 +21,7 @@ public class ShareClientPreferencePage extends FieldEditorPreferencePage impleme
|
|||||||
addField(new BooleanFieldEditor(ShareClientPreferences.SHOW_CURSORS, "Show other users' cursors (TODO)", getFieldEditorParent()));
|
addField(new BooleanFieldEditor(ShareClientPreferences.SHOW_CURSORS, "Show other users' cursors (TODO)", getFieldEditorParent()));
|
||||||
addField(new BooleanFieldEditor(ShareClientPreferences.ONLY_SHARE_SOURCE_FOLDERS, "Only share files in source folders (TODO)", getFieldEditorParent()));
|
addField(new BooleanFieldEditor(ShareClientPreferences.ONLY_SHARE_SOURCE_FOLDERS, "Only share files in source folders (TODO)", getFieldEditorParent()));
|
||||||
addField(new BooleanFieldEditor(ShareClientPreferences.USE_SHARECLIENT_PROJECT_CONFIG, "Use configuration from .shareclient file in the project (TODO)", getFieldEditorParent()));
|
addField(new BooleanFieldEditor(ShareClientPreferences.USE_SHARECLIENT_PROJECT_CONFIG, "Use configuration from .shareclient file in the project (TODO)", getFieldEditorParent()));
|
||||||
|
addField(new BooleanFieldEditor(ShareClientPreferences.DEBUG_MODE, "Debug mode", getFieldEditorParent())); // TODO: update view on setting change
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -7,6 +7,7 @@ public class ShareClientPreferences {
|
|||||||
USERNAME = "username",
|
USERNAME = "username",
|
||||||
SHOW_CURSORS = "showCursors",
|
SHOW_CURSORS = "showCursors",
|
||||||
ONLY_SHARE_SOURCE_FOLDERS = "onlyShareSourceFolders", // TODO: option to only share source folders of projects?
|
ONLY_SHARE_SOURCE_FOLDERS = "onlyShareSourceFolders", // TODO: option to only share source folders of projects?
|
||||||
USE_SHARECLIENT_PROJECT_CONFIG = "useShareClientConfig"; // TODO: option to enable a .shareclient project-specific config file
|
USE_SHARECLIENT_PROJECT_CONFIG = "useShareClientConfig", // TODO: option to enable a .shareclient project-specific config file
|
||||||
|
DEBUG_MODE = "debugMode";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,13 @@ import java.util.Map;
|
|||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import org.eclipse.core.resources.IProject;
|
import org.eclipse.core.resources.IProject;
|
||||||
|
import org.eclipse.jface.text.BadLocationException;
|
||||||
|
import org.eclipse.jface.text.IDocument;
|
||||||
|
|
||||||
import me.mrletsplay.shareclient.ShareClient;
|
import me.mrletsplay.shareclient.ShareClient;
|
||||||
|
import me.mrletsplay.shareclient.util.listeners.ShareClientDocumentListener;
|
||||||
import me.mrletsplay.shareclientcore.connection.RemoteConnection;
|
import me.mrletsplay.shareclientcore.connection.RemoteConnection;
|
||||||
|
import me.mrletsplay.shareclientcore.document.DocumentListener;
|
||||||
import me.mrletsplay.shareclientcore.document.SharedDocument;
|
import me.mrletsplay.shareclientcore.document.SharedDocument;
|
||||||
|
|
||||||
public class ShareSession {
|
public class ShareSession {
|
||||||
@ -79,51 +83,48 @@ public class ShareSession {
|
|||||||
|
|
||||||
public SharedDocument getOrCreateSharedDocument(ProjectRelativePath path, Supplier<byte[]> initialContents) {
|
public SharedDocument getOrCreateSharedDocument(ProjectRelativePath path, Supplier<byte[]> initialContents) {
|
||||||
return sharedDocuments.computeIfAbsent(path, p -> {
|
return sharedDocuments.computeIfAbsent(path, p -> {
|
||||||
SharedDocument doc = new SharedDocument(connection, path.toString(), initialContents.get());
|
SharedDocument doc = new SharedDocument(connection, path.toString(), initialContents == null ? null : initialContents.get());
|
||||||
|
|
||||||
// doc.addListener(new DocumentListener() {
|
doc.addListener(new DocumentListener() {
|
||||||
//
|
|
||||||
// @Override
|
@Override
|
||||||
// public void onInsert(int index, byte character) {
|
public void onInsert(int index, byte character) {
|
||||||
// // FIXME: potential desync. If changes arrive while waiting for asyncExec to execute, it will insert at the wrong position
|
// TODO: assert that we are on the UI thread
|
||||||
// Display.getDefault().asyncExec(() -> {
|
ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListener(path);
|
||||||
// ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListener(path);
|
if(documentListener != null) {
|
||||||
// if(documentListener != null) {
|
IDocument document = documentListener.getDocument();
|
||||||
// IDocument document = documentListener.getDocument();
|
|
||||||
//
|
documentListener.setIgnoreChanges(true);
|
||||||
// documentListener.setIgnoreChanges(true);
|
try {
|
||||||
// try {
|
document.replace(index, 0, String.valueOf((char) character)); // FIXME: dangerous! will explode on multi-byte characters
|
||||||
// document.replace(index, 0, String.valueOf(character));
|
} catch (BadLocationException e) {
|
||||||
// } catch (BadLocationException e) {
|
e.printStackTrace();
|
||||||
// e.printStackTrace();
|
// TODO: treat as inconsistency
|
||||||
// // TODO: treat as inconsistency
|
// MessageDialog.openError(null, "Share Client", "Failed to update document: " + e.toString());
|
||||||
//// MessageDialog.openError(null, "Share Client", "Failed to update document: " + e.toString());
|
}
|
||||||
// }
|
documentListener.setIgnoreChanges(false);
|
||||||
// documentListener.setIgnoreChanges(false);
|
}
|
||||||
// }
|
}
|
||||||
// });
|
|
||||||
// }
|
@Override
|
||||||
//
|
public void onDelete(int index) {
|
||||||
// @Override
|
// TODO: assert that we are on the UI thread
|
||||||
// public void onDelete(int index) {
|
ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListener(path);
|
||||||
// Display.getDefault().asyncExec(() -> {
|
if(documentListener != null) {
|
||||||
// ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListener(path);
|
IDocument document = documentListener.getDocument();
|
||||||
// if(documentListener != null) {
|
|
||||||
// IDocument document = documentListener.getDocument();
|
documentListener.setIgnoreChanges(true);
|
||||||
//
|
try {
|
||||||
// documentListener.setIgnoreChanges(true);
|
document.replace(index, 1, "");
|
||||||
// try {
|
} catch (BadLocationException e) {
|
||||||
// document.replace(index, 1, "");
|
e.printStackTrace();
|
||||||
// } catch (BadLocationException e) {
|
// TODO: treat as inconsistency
|
||||||
// e.printStackTrace();
|
// MessageDialog.openError(null, "Share Client", "Failed to update document: " + e.toString());
|
||||||
// // TODO: treat as inconsistency
|
}
|
||||||
//// MessageDialog.openError(null, "Share Client", "Failed to update document: " + e.toString());
|
documentListener.setIgnoreChanges(false);
|
||||||
// }
|
}
|
||||||
// documentListener.setIgnoreChanges(false);
|
}
|
||||||
// }
|
});
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
return doc;
|
return doc;
|
||||||
});
|
});
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package me.mrletsplay.shareclient.util.listeners;
|
package me.mrletsplay.shareclient.util.listeners;
|
||||||
|
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import org.eclipse.jface.text.DocumentEvent;
|
import org.eclipse.jface.text.DocumentEvent;
|
||||||
import org.eclipse.jface.text.IDocument;
|
import org.eclipse.jface.text.IDocument;
|
||||||
import org.eclipse.jface.text.IDocumentListener;
|
import org.eclipse.jface.text.IDocumentListener;
|
||||||
import org.eclipse.swt.widgets.Display;
|
|
||||||
|
|
||||||
import me.mrletsplay.shareclient.ShareClient;
|
import me.mrletsplay.shareclient.ShareClient;
|
||||||
import me.mrletsplay.shareclient.util.ProjectAndPath;
|
import me.mrletsplay.shareclient.util.ProjectAndPath;
|
||||||
@ -38,20 +39,20 @@ public class ShareClientDocumentListener implements IDocumentListener {
|
|||||||
|
|
||||||
System.out.println("UPDATE ON THREAD " + Thread.currentThread());
|
System.out.println("UPDATE ON THREAD " + Thread.currentThread());
|
||||||
|
|
||||||
Display.getDefault().asyncExec(() -> {
|
// FIXME: bug somewhere here, can cause discrepancies in local editor when editing while changes are coming in
|
||||||
ShareSession session = ShareClient.getDefault().getActiveSession();
|
|
||||||
if(session == null) return;
|
|
||||||
|
|
||||||
SharedProject sharedProject = session.getSharedProject(path.project());
|
ShareSession session = ShareClient.getDefault().getActiveSession();
|
||||||
if(sharedProject == null) return;
|
if(session == null) return;
|
||||||
|
|
||||||
SharedDocument doc = session.getOrCreateSharedDocument(new ProjectRelativePath(sharedProject.getRemoteName(), path.path()), () -> event.fDocument.get());
|
SharedProject sharedProject = session.getSharedProject(path.project());
|
||||||
if(event.getLength() > 0) {
|
if(sharedProject == null) return;
|
||||||
doc.localDelete(event.getOffset(), event.getLength());
|
|
||||||
}
|
|
||||||
|
|
||||||
doc.localInsert(event.getOffset(), event.getText());
|
SharedDocument doc = session.getOrCreateSharedDocument(new ProjectRelativePath(sharedProject.getRemoteName(), path.path()), () -> event.fDocument.get().getBytes(StandardCharsets.UTF_8));
|
||||||
});
|
if(event.getLength() > 0) {
|
||||||
|
doc.localDelete(event.getOffset(), event.getLength());
|
||||||
|
}
|
||||||
|
|
||||||
|
doc.localInsert(event.getOffset(), event.getText());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package me.mrletsplay.shareclient.views;
|
||||||
|
|
||||||
|
import org.eclipse.jface.dialogs.TitleAreaDialog;
|
||||||
|
import org.eclipse.swt.SWT;
|
||||||
|
import org.eclipse.swt.layout.GridData;
|
||||||
|
import org.eclipse.swt.widgets.Composite;
|
||||||
|
import org.eclipse.swt.widgets.Control;
|
||||||
|
import org.eclipse.swt.widgets.Shell;
|
||||||
|
import org.eclipse.swt.widgets.Text;
|
||||||
|
|
||||||
|
public class ScrollableDialog extends TitleAreaDialog {
|
||||||
|
|
||||||
|
private String text;
|
||||||
|
|
||||||
|
public ScrollableDialog(Shell parentShell, String title, String text) {
|
||||||
|
super(parentShell);
|
||||||
|
setTitle(title);
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Control createDialogArea(Composite parent) {
|
||||||
|
Composite composite = (Composite) super.createDialogArea(parent);
|
||||||
|
|
||||||
|
GridData gridData = new GridData();
|
||||||
|
gridData.grabExcessHorizontalSpace = true;
|
||||||
|
gridData.horizontalAlignment = GridData.FILL;
|
||||||
|
gridData.grabExcessVerticalSpace = true;
|
||||||
|
gridData.verticalAlignment = GridData.FILL;
|
||||||
|
|
||||||
|
Text scrollable = new Text(composite, SWT.BORDER | SWT.V_SCROLL);
|
||||||
|
scrollable.setLayoutData(gridData);
|
||||||
|
scrollable.setText(text);
|
||||||
|
|
||||||
|
return composite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean isResizable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -31,6 +31,7 @@ import org.eclipse.ui.dialogs.PreferencesUtil;
|
|||||||
import org.eclipse.ui.part.ViewPart;
|
import org.eclipse.ui.part.ViewPart;
|
||||||
|
|
||||||
import me.mrletsplay.shareclient.ShareClient;
|
import me.mrletsplay.shareclient.ShareClient;
|
||||||
|
import me.mrletsplay.shareclient.ShareClientPreferences;
|
||||||
import me.mrletsplay.shareclient.util.ShareSession;
|
import me.mrletsplay.shareclient.util.ShareSession;
|
||||||
import me.mrletsplay.shareclientcore.connection.ConnectionException;
|
import me.mrletsplay.shareclientcore.connection.ConnectionException;
|
||||||
import me.mrletsplay.shareclientcore.connection.RemoteConnection;
|
import me.mrletsplay.shareclientcore.connection.RemoteConnection;
|
||||||
@ -161,6 +162,33 @@ public class ShareView extends ViewPart {
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Action debug = new Action("Debug", ImageDescriptor.createFromFile(ShareView.class, "/icons/bug.png")) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
ShareSession session = ShareClient.getDefault().getActiveSession();
|
||||||
|
if(session == null) {
|
||||||
|
MessageDialog.openInformation(null, "Debug", "No session running");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
session.getSharedDocuments().forEach((path, document) -> {
|
||||||
|
sb.append("--- Document: ").append(path).append('\n');
|
||||||
|
|
||||||
|
sb.append(document.getContentsAsString());
|
||||||
|
|
||||||
|
sb.append('\n');
|
||||||
|
sb.append('\n');
|
||||||
|
});
|
||||||
|
|
||||||
|
// MessageDialog.openInformation(null, "Debug Info", sb.toString());
|
||||||
|
new ScrollableDialog(null, "Debug Info", sb.toString()).open();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
if(ShareClient.getDefault().getActiveSession() == null) {
|
if(ShareClient.getDefault().getActiveSession() == null) {
|
||||||
toolbars.add(joinSession);
|
toolbars.add(joinSession);
|
||||||
}else {
|
}else {
|
||||||
@ -170,6 +198,10 @@ public class ShareView extends ViewPart {
|
|||||||
|
|
||||||
toolbars.add(showSettings);
|
toolbars.add(showSettings);
|
||||||
|
|
||||||
|
if(ShareClient.getDefault().getPreferenceStore().getBoolean(ShareClientPreferences.DEBUG_MODE)) {
|
||||||
|
toolbars.add(debug);
|
||||||
|
}
|
||||||
|
|
||||||
bars.updateActionBars();
|
bars.updateActionBars();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user