From 09d7d80d2930c8fe539fa9c6dfbff51e547a94e9 Mon Sep 17 00:00:00 2001 From: MrLetsplay Date: Wed, 5 Jun 2024 22:06:46 +0200 Subject: [PATCH] Add debug dialog, Use bytes everywhere, Sync somewhat works (WIP) --- icons/bug.png | Bin 0 -> 502 bytes icons/bug@2x.png | Bin 0 -> 945 bytes .../mrletsplay/shareclient/ShareClient.java | 41 ++++---- .../ShareClientPreferencePage.java | 1 + .../shareclient/ShareClientPreferences.java | 3 +- .../shareclient/util/ShareSession.java | 91 +++++++++--------- .../ShareClientDocumentListener.java | 25 ++--- .../shareclient/views/ScrollableDialog.java | 43 +++++++++ .../shareclient/views/ShareView.java | 32 ++++++ 9 files changed, 158 insertions(+), 78 deletions(-) create mode 100644 icons/bug.png create mode 100644 icons/bug@2x.png create mode 100644 src/main/java/me/mrletsplay/shareclient/views/ScrollableDialog.java diff --git a/icons/bug.png b/icons/bug.png new file mode 100644 index 0000000000000000000000000000000000000000..71bb156abfd45e74f51065774389df1c5741bdf2 GIT binary patch literal 502 zcmVJ1Su&@#JTKxm8ZowkEn}qxW3Swn%;Wf4Jd1+7P|>51n(*%Vx&vUnSfcJj;WJ6ID zA5IE4tsftuv6;4_HWPpl!cMC*vowz5y|!NWkSa;nR!QgFb)Y)MA9q#NRGY+L$jD(1T92o1y52eQYiHn3VJA7+tQKRB(p=bxztdVf;}1L zq|(wJq9CzhGqY)hjD=Q_;z@4?5h;`+6>Fd^qS8x(nb6RAuZNwf({(ocRs!jvztj8k zeZTje@9)nW(OR>fX07Y(j|c!|Q{eRU^llM(YjJV$jj^$@J4Ki%iZ(7UFV85Y&Uv19 z!NjkaBw-kauV}5$0~>*_dwY8iIF7T@dim1Q(yWNQ2CM-4Yqi=poqQW9pp?2MB7Xz@ zz^ic_f7xg>-cORGUqr;RteZ)ae3)Ji@R!#5R}g`(xo4@)+e)l%d&2{uKPpl zUF{@&S45oFMXmL>l}hE1<2Xs{-stG)LKH;@<2b$qyaenJk=Yz10{%mwn?|ISQb&s} z&&+Y0#ImfTfbj#xicOpQo|EGBPr9jb@1619rBYnR8wD)bQ}|FF_Do1-1eU zTI<89pIgQR^05kn;4Dq~=W~#qTI&ZwBqI06TI=f~vK8p{JTF>J0h17HpqWG~ouR4I zf3X`nVG9)q!*I6s3-ji>?(3#xf*{Z|6^e?^J+!akTo%(JC0hFgtJJxOWH~TrOu%*B zQ`tC^u*Hh{L}8tG3YSR;mS`&Rm;sg)o3-qJI$_%-VZP2YlG}XWx5mfERlZMTmeslM z`&PADeLh_-g<+U=!@0b&s}d-xb6|^Y+k1cuMX}DTTCMH_o=m5kGC%n~k+-g@Ktw*R z*XvKU+ix@)PXiNO`80WsgCIB!ypvA<5Rtd%=jSh`;lTHO%eL*kBJv5ag=QXn?0Met zH6buDG11f4*Y_3ha&Gxg;Bq>C0eB*}cgeQxJp%&+ch`gf=}%`I$0vXzK&cH(X{|r1 zR4N~=_G String.valueOf(c.value())) - .collect(Collectors.joining()); - SharedDocument sharedDocument = activeSession.getOrCreateSharedDocument(path, () -> content); - // TODO: update sharedDocuments + SharedDocument sharedDocument = activeSession.getOrCreateSharedDocument(path, null /* because the content will be replaced afterwards anyway */); + sharedDocument.clear(); + for(Char c : sync.content()) sharedDocument.getCharBag().add(c); 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); if(listener != null) listener.setIgnoreChanges(false); } catch (IOException | CoreException e) { @@ -299,17 +298,13 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, Di 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 - SharedDocument doc = activeSession.getOrCreateSharedDocument(path, () -> { - try { - return Files.readString(resolvePath(path)); - }catch(IOException e) { - MessageDialog.openError(null, "Share Client", "Failed to read file: " + e.toString()); - throw new RuntimeException(e); - } - }); + if(doc == null) { + // TODO: show proper synchronization issue warning, because all received changes should be in previously shared documents + MessageDialog.openError(null, "Share Client", "Synchronization issue (received change in non-shared document)"); + return; + } doc.onMessage(message); } @@ -333,7 +328,7 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, Di for(Map.Entry en : getProjectFiles(shared).entrySet()) { activeSession.getOrCreateSharedDocument(en.getKey(), () -> { try { - return Files.readString(en.getValue(), StandardCharsets.UTF_8); + return Files.readAllBytes(en.getValue()); } catch (IOException e) { e.printStackTrace(); throw new RuntimeException(e); @@ -364,7 +359,13 @@ public class ShareClient extends AbstractUIPlugin implements MessageListener, Di 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()); if(sharedProject == null) return null; Path projectLocation = sharedProject.getLocal().getLocation().toPath(); diff --git a/src/main/java/me/mrletsplay/shareclient/ShareClientPreferencePage.java b/src/main/java/me/mrletsplay/shareclient/ShareClientPreferencePage.java index b1738c0..6e85db3 100644 --- a/src/main/java/me/mrletsplay/shareclient/ShareClientPreferencePage.java +++ b/src/main/java/me/mrletsplay/shareclient/ShareClientPreferencePage.java @@ -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.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.DEBUG_MODE, "Debug mode", getFieldEditorParent())); // TODO: update view on setting change } @Override diff --git a/src/main/java/me/mrletsplay/shareclient/ShareClientPreferences.java b/src/main/java/me/mrletsplay/shareclient/ShareClientPreferences.java index 65b3ad4..72104d2 100644 --- a/src/main/java/me/mrletsplay/shareclient/ShareClientPreferences.java +++ b/src/main/java/me/mrletsplay/shareclient/ShareClientPreferences.java @@ -7,6 +7,7 @@ public class ShareClientPreferences { USERNAME = "username", SHOW_CURSORS = "showCursors", 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"; } diff --git a/src/main/java/me/mrletsplay/shareclient/util/ShareSession.java b/src/main/java/me/mrletsplay/shareclient/util/ShareSession.java index 1b6f3d0..e2b15db 100644 --- a/src/main/java/me/mrletsplay/shareclient/util/ShareSession.java +++ b/src/main/java/me/mrletsplay/shareclient/util/ShareSession.java @@ -8,9 +8,13 @@ import java.util.Map; import java.util.function.Supplier; 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.util.listeners.ShareClientDocumentListener; import me.mrletsplay.shareclientcore.connection.RemoteConnection; +import me.mrletsplay.shareclientcore.document.DocumentListener; import me.mrletsplay.shareclientcore.document.SharedDocument; public class ShareSession { @@ -79,51 +83,48 @@ public class ShareSession { public SharedDocument getOrCreateSharedDocument(ProjectRelativePath path, Supplier initialContents) { return sharedDocuments.computeIfAbsent(path, p -> { - SharedDocument doc = new SharedDocument(connection, path.toString(), initialContents.get()); - -// doc.addListener(new DocumentListener() { -// -// @Override -// 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 -// Display.getDefault().asyncExec(() -> { -// ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListener(path); -// if(documentListener != null) { -// IDocument document = documentListener.getDocument(); -// -// documentListener.setIgnoreChanges(true); -// try { -// document.replace(index, 0, String.valueOf(character)); -// } catch (BadLocationException e) { -// e.printStackTrace(); -// // TODO: treat as inconsistency -//// MessageDialog.openError(null, "Share Client", "Failed to update document: " + e.toString()); -// } -// documentListener.setIgnoreChanges(false); -// } -// }); -// } -// -// @Override -// public void onDelete(int index) { -// Display.getDefault().asyncExec(() -> { -// ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListener(path); -// if(documentListener != null) { -// IDocument document = documentListener.getDocument(); -// -// documentListener.setIgnoreChanges(true); -// try { -// document.replace(index, 1, ""); -// } catch (BadLocationException e) { -// e.printStackTrace(); -// // TODO: treat as inconsistency -//// MessageDialog.openError(null, "Share Client", "Failed to update document: " + e.toString()); -// } -// documentListener.setIgnoreChanges(false); -// } -// }); -// } -// }); + SharedDocument doc = new SharedDocument(connection, path.toString(), initialContents == null ? null : initialContents.get()); + + doc.addListener(new DocumentListener() { + + @Override + public void onInsert(int index, byte character) { + // TODO: assert that we are on the UI thread + ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListener(path); + if(documentListener != null) { + IDocument document = documentListener.getDocument(); + + documentListener.setIgnoreChanges(true); + try { + document.replace(index, 0, String.valueOf((char) character)); // FIXME: dangerous! will explode on multi-byte characters + } catch (BadLocationException e) { + e.printStackTrace(); + // TODO: treat as inconsistency +// MessageDialog.openError(null, "Share Client", "Failed to update document: " + e.toString()); + } + documentListener.setIgnoreChanges(false); + } + } + + @Override + public void onDelete(int index) { + // TODO: assert that we are on the UI thread + ShareClientDocumentListener documentListener = ShareClient.getDefault().getPartListener().getListener(path); + if(documentListener != null) { + IDocument document = documentListener.getDocument(); + + documentListener.setIgnoreChanges(true); + try { + document.replace(index, 1, ""); + } catch (BadLocationException e) { + e.printStackTrace(); + // TODO: treat as inconsistency +// MessageDialog.openError(null, "Share Client", "Failed to update document: " + e.toString()); + } + documentListener.setIgnoreChanges(false); + } + } + }); return doc; }); 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 0b567af..8e1d89f 100644 --- a/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientDocumentListener.java +++ b/src/main/java/me/mrletsplay/shareclient/util/listeners/ShareClientDocumentListener.java @@ -1,9 +1,10 @@ package me.mrletsplay.shareclient.util.listeners; +import java.nio.charset.StandardCharsets; + import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IDocumentListener; -import org.eclipse.swt.widgets.Display; import me.mrletsplay.shareclient.ShareClient; import me.mrletsplay.shareclient.util.ProjectAndPath; @@ -38,20 +39,20 @@ public class ShareClientDocumentListener implements IDocumentListener { System.out.println("UPDATE ON THREAD " + Thread.currentThread()); - Display.getDefault().asyncExec(() -> { - ShareSession session = ShareClient.getDefault().getActiveSession(); - if(session == null) return; + // FIXME: bug somewhere here, can cause discrepancies in local editor when editing while changes are coming in - SharedProject sharedProject = session.getSharedProject(path.project()); - if(sharedProject == null) return; + ShareSession session = ShareClient.getDefault().getActiveSession(); + if(session == 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()); - } + SharedProject sharedProject = session.getSharedProject(path.project()); + if(sharedProject == null) return; - 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 diff --git a/src/main/java/me/mrletsplay/shareclient/views/ScrollableDialog.java b/src/main/java/me/mrletsplay/shareclient/views/ScrollableDialog.java new file mode 100644 index 0000000..81d3aec --- /dev/null +++ b/src/main/java/me/mrletsplay/shareclient/views/ScrollableDialog.java @@ -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; + } + +} diff --git a/src/main/java/me/mrletsplay/shareclient/views/ShareView.java b/src/main/java/me/mrletsplay/shareclient/views/ShareView.java index 5e884e5..73e2544 100644 --- a/src/main/java/me/mrletsplay/shareclient/views/ShareView.java +++ b/src/main/java/me/mrletsplay/shareclient/views/ShareView.java @@ -31,6 +31,7 @@ import org.eclipse.ui.dialogs.PreferencesUtil; import org.eclipse.ui.part.ViewPart; import me.mrletsplay.shareclient.ShareClient; +import me.mrletsplay.shareclient.ShareClientPreferences; import me.mrletsplay.shareclient.util.ShareSession; import me.mrletsplay.shareclientcore.connection.ConnectionException; 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) { toolbars.add(joinSession); }else { @@ -170,6 +198,10 @@ public class ShareView extends ViewPart { toolbars.add(showSettings); + if(ShareClient.getDefault().getPreferenceStore().getBoolean(ShareClientPreferences.DEBUG_MODE)) { + toolbars.add(debug); + } + bars.updateActionBars(); }