[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/crdt d20b384 16/80: document and improvements
From: |
ELPA Syncer |
Subject: |
[elpa] externals/crdt d20b384 16/80: document and improvements |
Date: |
Sat, 28 Aug 2021 10:57:32 -0400 (EDT) |
branch: externals/crdt
commit d20b3843c757abfd64d5a353eade76210ed24cc8
Author: Qiantan Hong <qhong@mit.edu>
Commit: Qiantan Hong <qhong@mit.edu>
document and improvements
- HACKING.org
- add crdt--session-name session variable
- synchronize session name over network
---
HACKING.org | 93 +++++++++++++++++++++++++
crdt.el | 227 +++++++++++++++++++++++-------------------------------------
2 files changed, 179 insertions(+), 141 deletions(-)
diff --git a/HACKING.org b/HACKING.org
new file mode 100644
index 0000000..d731c31
--- /dev/null
+++ b/HACKING.org
@@ -0,0 +1,93 @@
+* Algorithm
+
+This packages implements the Logoot split algorithm
+~André, Luc, et al. "Supporting adaptable granularity of changes for
massive-scale collaborative editing." 9th IEEE International Conference on
Collaborative Computing: Networking, Applications and Worksharing. IEEE, 2013.~
+
+The CRDT-ID blocks are implemented by text property ='crdt-id=. A continous
range of text with the same ='crdt-id'= property represent a CRDT-ID block. The
='crdt-id= is a a cons of =(ID-STRING . END-OF-BLOCK-P)=, where
+=ID-STRING= represent the CRDT-ID of the leftmost character in the block. If
=END-OF-BLOCK-P= is =NIL=, the block is a non-rightmost segment splitted from a
larger block, so insertion at the right of this block shouldn't be merged into
the block by sharing the base CRDT-ID and increasing offset.
+
+=ID-STRING= is a unibyte string representing a CRDT-ID (for efficient
comparison).
+Every two bytes represent a big endian encoded integer.
+For base IDs, last two bytes are always representing site ID.
+Stored strings are BASE-ID:OFFSETs. So the last two bytes represent offset,
+and second last two bytes represent site ID.
+
+* Session structure
+
+=CRDT--SESSION-LIST= is a list of "CRDT status buffer"s.
+Currently those buffers are always empty, but they have some buffer local
variables,
+which are used as "session variables" that can be accessed from any buffer
shared in the same session.
+For a buffer shared in some session, this buffer always has its buffer local
variable
+=CRDT--STATUS-BUFFER= set to a CRDT status buffer. It can then access any
session variables through it.
+For a network process dedicated to a session, its ='status-buffer= process
property is always set to the status buffer for that session.
+The macro =CRDT--DEFVAR-SESSION= do the chores of defining a buffer local
variable for status buffer,
+and creating a function (together with =SETF= setter) with the same name as
the variables, and can
+be invoked with no argument in any CRDT shared buffer to access or modify that
session variable.
+
+
+* Protocol
+ Text-based version
+ (it should be easy to migrate to a binary version. Using text for better
debugging for now)
+ Every message takes the form (type . body)
+ type can be: insert delete cursor hello challenge sync desync
overlay-(add,move,put,remove)
+ - insert
+ body takes the form =(buffer-name crdt-id position-hint content)=
+ - =position-hint= is the buffer position where the operation happens at
the site
+ which generates the operation. Then we can play the trick that start
search
+ near this position at other sites to speedup CRDT ID search
+ - =content= is the string to be inserted
+ - delete
+ body takes the form =(buffer-name position-hint (crdt-id . length)*)=
+ - cursor
+ body takes the form
+ =(site-id point-position-hint point-crdt-id mark-position-hint
mark-crdt-id)=
+ =*-crdt-id= can be either a CRDT ID, or
+ - =nil=, which means clear the point/mark
+ - =""=, which means =(point-max)=
+ - contact
+ body takes the form
+ =(site-id name address port)=
+ when name is =nil=, clear the contact for this =site-id=
+ - focus
+ body takes the form =(site-id buffer-name)=
+ - hello
+ This message is sent from client to server, when a client connect to the
server.
+ body takes the form =(client-name &optional response)=
+ - challenge
+ body takes the form =(salt)=
+ - login
+ It's always sent after server receives a hello message.
+ Assigns an ID to the client
+ body takes the form =(site-id session-name)=.
+ - sync
+ This message is sent from server to client to get it sync to the state on
the server.
+ Might be used for error recovery or other optimization in the future.
+ One optimization I have in mind is let server try to merge all CRDT item
into a single
+ one and try to synchronize this state to clients at best effort.
+ body takes the form =(buffer-name major-mode content . crdt-id-list)=
+ - =major-mode= is the major mode used at the server site
+ - =content= is the string in the buffer
+ - =crdt-id-list= is generated from =CRDT--DUMP-IDS=
+ - desync
+ Indicates that the server has stopped sharing a buffer.
+ body takes the form =(buffer-name)=
+ - overlay-add
+ body takes the form
+#+BEGIN_SRC
+(buffer-name site-id logical-clock species
+ front-advance rear-advance
+ start-position-hint start-crdt-id
+ end-position-hint end-crdt-id)
+#+END_SRC
+ - overlay-move
+ body takes the form
+#+BEGIN_SRC
+(buffer-name site-id logical-clock
+ start-position-hint start-crdt-id
+ end-position-hint end-crdt-id)
+#+END_SRC
+ - overlay-put
+ body takes the form =(buffer-name site-id logical-clock prop value)=
+ - overlay-remove
+ body takes the form =(buffer-name site-id logical-clock)=
+
diff --git a/crdt.el b/crdt.el
index 5a5753d..015cde2 100644
--- a/crdt.el
+++ b/crdt.el
@@ -24,73 +24,6 @@
;; This package provides a collaborative editing environment for Emacs.
;;; Code:
-
-;; * Algorithm
-;; This packages implements the Logoot split algorithm
-;; André, Luc, et al.
-;; "Supporting adaptable granularity of changes for massive-scale
collaborative editing."
-;; 9th IEEE International Conference on Collaborative Computing:
Networking, Applications and Worksharing.
-;; IEEE, 2013.
-;; * Protocol
-;; Text-based version
-;; (it should be easy to migrate to a binary version. Using text for better
debugging for now)
-;; Every message takes the form (type . body)
-;; type can be: insert delete cursor hello challenge sync overlay
-;; - insert
-;; body takes the form (buffer-name crdt-id position-hint content)
-;; - position-hint is the buffer position where the operation happens at
the site
-;; which generates the operation. Then we can play the trick that start
search
-;; near this position at other sites to speedup crdt-id search
-;; - content is the string to be inserted
-;; - delete
-;; body takes the form (buffer-name position-hint (crdt-id . length)*)
-;; - cursor
-;; body takes the form
-;; (site-id point-position-hint point-crdt-id mark-position-hint
mark-crdt-id)
-;; *-crdt-id can be either a CRDT ID, or
-;; - nil, which means clear the point/mark
-;; - "", which means (point-max)
-;; - contact
-;; body takes the form
-;; (site-id name address port)
-;; when name is nil, clear the contact for this site-id
-;; - focus
-;; body takes the form (site-id buffer-name)
-;; - hello
-;; This message is sent from client to server, when a client connect to
the server.
-;; body takes the form (client-name &optional response)
-;; - challenge
-;; body takes the form (salt)
-;; - login
-;; It's always sent after server receives a hello message.
-;; Assigns an ID to the client
-;; body takes the form (site-id).
-;; - sync
-;; This message is sent from server to client to get it sync to the state
on the server.
-;; Might be used for error recovery or other optimization in the future.
-;; One optimization I have in mind is let server try to merge all CRDT
item into a single
-;; one and try to synchronize this state to clients at best effort.
-;; body takes the form (buffer-name major-mode content . crdt-id-list)
-;; - major-mode is the major mode used at the server site
-;; - content is the string in the buffer
-;; - crdt-id-list is generated from CRDT--DUMP-IDS
-;; - desync
-;; Indicates that the server has stopped sharing a buffer.
-;; body takes the form (buffer-name)
-;; - overlay-add
-;; body takes the form (buffer-name site-id logical-clock species
-;; front-advance rear-advance
-;; start-position-hint start-crdt-id
-;; end-position-hint end-crdt-id)
-;; - overlay-move
-;; body takes the form (buffer-name site-id logical-clock
-;; start-position-hint start-crdt-id
-;; end-position-hint end-crdt-id)
-;; - overlay-put
-;; body takes the form (buffer-name site-id logical-clock prop value)
-;; - overlay-remove
-;; body takes the form (buffer-name site-id logical-clock)
-
;;; Customs
(defgroup crdt nil
@@ -380,13 +313,15 @@ to avoid recusive calling of CRDT synchronization
functions.")
(crdt--defvar-session crdt--local-name nil)
+(crdt--defvar-session crdt--session-name nil)
+
(crdt--defvar-session crdt--focused-buffer-name nil)
(crdt--defvar-session crdt--user-menu-buffer nil)
(crdt--defvar-session crdt--buffer-menu-buffer nil)
-(defvar crdt--session-alist nil)
+(defvar crdt--session-list nil)
(defvar crdt--session-menu-buffer nil)
@@ -434,13 +369,13 @@ to avoid recusive calling of CRDT synchronization
functions.")
(defun crdt--session-menu-goto ()
(interactive)
(with-current-buffer
- (process-get (tabulated-list-get-id) 'status-buffer)
+ (tabulated-list-get-id)
(crdt-list-buffer)))
(defun crdt--session-menu-kill ()
(interactive)
(with-current-buffer
- (process-get (tabulated-list-get-id) 'status-buffer)
+ (tabulated-list-get-id)
(crdt-stop-session)))
(defvar crdt-session-menu-mode-map
@@ -453,7 +388,7 @@ to avoid recusive calling of CRDT synchronization
functions.")
"CRDT User List"
(setq tabulated-list-format [("Session Name" 15 t)
("Role" 7 t)
- ("My Display Name" 15 t)
+ ("My Name" 15 t)
("Buffers" 15 t)
("Users" 15 t)]))
@@ -474,24 +409,24 @@ Otherwise use a dedicated buffer for displaying active
users on CRDT-BUFFER."
(with-current-buffer display-buffer
(crdt-session-menu-mode)
(setq tabulated-list-entries nil)
- (mapc (lambda (pair)
- (cl-destructuring-bind (name . s) pair
- (push
- (list s (with-current-buffer (process-get s 'status-buffer)
- (vector name (if (process-contact s :server) "Server"
"Client")
- crdt--local-name
- (mapconcat (lambda (v) (format "%s" v))
- (hash-table-keys
crdt--buffer-table)
- ", ")
- (mapconcat (lambda (v) (format "%s" v))
- (let (users)
- (maphash (lambda (k v)
- (push
(crdt--contact-metadata-display-name v) users))
- crdt--contact-table)
- (cons crdt--local-name users))
- ", "))))
- tabulated-list-entries)))
- crdt--session-alist)
+ (mapc (lambda (status-buffer)
+ (push
+ (list status-buffer (with-current-buffer status-buffer
+ (vector crdt--session-name
+ (if (crdt--server-p) "Server"
"Client")
+ crdt--local-name
+ (mapconcat (lambda (v) (format "%s"
v))
+ (hash-table-keys
crdt--buffer-table)
+ ", ")
+ (mapconcat (lambda (v) (format "%s"
v))
+ (let (users)
+ (maphash (lambda (k v)
+ (push
(crdt--contact-metadata-display-name v) users))
+
crdt--contact-table)
+ (cons crdt--local-name
users))
+ ", "))))
+ tabulated-list-entries))
+ crdt--session-list)
(tabulated-list-init-header)
(tabulated-list-print)))
@@ -533,7 +468,7 @@ Otherwise use a dedicated buffer for displaying active
users on CRDT-BUFFER."
(unless display-buffer
(unless (and (crdt--buffer-menu-buffer) (buffer-live-p
(crdt--buffer-menu-buffer)))
(setf (crdt--buffer-menu-buffer)
- (generate-new-buffer (concat (buffer-name (current-buffer))
+ (generate-new-buffer (concat (crdt--session-name)
" buffers")))
(crdt--assimilate-status-buffer (crdt--buffer-menu-buffer)))
(setq display-buffer (crdt--buffer-menu-buffer)))
@@ -618,8 +553,7 @@ Otherwise use a dedicated buffer for displaying active
users on CRDT-BUFFER."
(unless display-buffer
(unless (and (crdt--user-menu-buffer) (buffer-live-p
(crdt--user-menu-buffer)))
(setf (crdt--user-menu-buffer)
- (generate-new-buffer (concat (car (rassq (crdt--network-process)
crdt--session-alist))
- " users")))
+ (generate-new-buffer (concat (crdt--session-name) " users")))
(crdt--assimilate-status-buffer (crdt--user-menu-buffer)))
(setq display-buffer (crdt--user-menu-buffer)))
(with-current-buffer crdt--status-buffer
@@ -977,7 +911,9 @@ to server when WITHOUT is T."
(error "Used up client IDs. Need to implement allocation
algorithm"))
(process-put process 'client-id crdt--next-client-id)
(setq client-id crdt--next-client-id)
- (process-send-string process (crdt--format-message `(login
,client-id)))
+ (process-send-string process (crdt--format-message
+ `(login ,client-id
+ ,crdt--session-name)))
(cl-incf crdt--next-client-id))
(maphash (lambda (k buffer)
(with-current-buffer buffer
@@ -1130,11 +1066,12 @@ Must be called when CURRENT-BUFFER is a CRDT status
buffer."
(crdt--refresh-buffers-maybe))
(cl-defmethod crdt-process-message ((message (head login)) process)
- (cl-destructuring-bind (id) (cdr message)
+ (cl-destructuring-bind (id session-name) (cdr message)
(puthash 0 (crdt--make-contact-metadata nil nil
(process-contact process :host)
(process-contact process :service))
crdt--contact-table)
+ (setq crdt--session-name (concat session-name "@" crdt--session-name))
(setq crdt--local-id id)
(crdt--refresh-sessions-maybe)))
@@ -1250,9 +1187,10 @@ Must be called when CURRENT-BUFFER is a CRDT status
buffer."
crdt-default-name))
(defun crdt--share-buffer (buffer session)
- (if (process-contact session :server)
+ (if (process-contact (with-current-buffer session crdt--network-process)
+ :server)
(with-current-buffer buffer
- (setq crdt--status-buffer (process-get session 'status-buffer))
+ (setq crdt--status-buffer session)
(puthash (buffer-name buffer) buffer (crdt--buffer-table))
(setq crdt--buffer-network-name (buffer-name buffer))
(crdt-mode)
@@ -1271,23 +1209,33 @@ Must be called when CURRENT-BUFFER is a CRDT status
buffer."
(crdt--refresh-sessions-maybe))
(message "Only server can add new buffer.")))
+(defsubst crdt--get-session-names ()
+ (mapcar (lambda (s)
+ (with-current-buffer s crdt--session-name))
+ crdt--session-list))
+
+(defsubst crdt--get-session (name)
+ (cl-find name crdt--session-list
+ :test 'equal :key (lambda (s) (with-current-buffer s
crdt--session-name))))
+
(defun crdt-share-buffer (session-name)
"Share the current buffer in the CRDT session with name SESSION-NAME.
Create a new one if such a CRDT session doesn't exist.
If SESSION-NAME is empty, use the buffer name of the current buffer."
(interactive
- (list (let ((session-name (completing-read "Enter a session name (create if
not exist): "
- crdt--session-alist)))
- (unless (and session-name (> (length session-name) 0))
- (setq session-name (buffer-name (current-buffer))))
- session-name)))
- (if (and crdt-mode crdt--status-buffer)
- (message "Current buffer is already shared in a CRDT session.")
- (let ((session (assoc session-name crdt--session-alist)))
- (if session
- (crdt--share-buffer (current-buffer) (cdr session))
- (let ((port (read-from-minibuffer "Create new session on port (default
6530): " nil nil t nil "6530")))
- (crdt--share-buffer (current-buffer) (crdt-new-session port
session-name)))))))
+ (progn
+ (when (and crdt-mode crdt--status-buffer)
+ (error "Current buffer is already shared in a CRDT session"))
+ (list (let ((session-name (completing-read "Enter a session name (create
if not exist): "
+ (crdt--get-session-names))))
+ (unless (and session-name (> (length session-name) 0))
+ (setq session-name (buffer-name (current-buffer))))
+ session-name))))
+ (let ((session (crdt--get-session session-name)))
+ (if session
+ (crdt--share-buffer (current-buffer) session)
+ (let ((port (read-from-minibuffer "Create new session on port (default
6530): " nil nil t nil "6530")))
+ (crdt--share-buffer (current-buffer) (crdt-new-session port
session-name))))))
(defun crdt-stop-share-buffer ()
"Stop sharing the current buffer."
@@ -1334,45 +1282,41 @@ If SESSION-NAME is empty, use the buffer name of the
current buffer."
(setq crdt--local-name display-name)
(setq crdt--contact-table (make-hash-table :test 'equal))
(setq crdt--buffer-table (make-hash-table :test 'equal))
+ (setq crdt--session-name session-name)
(setq crdt--status-buffer (current-buffer))
- crdt--network-process)))
- (push (cons session-name new-session) crdt--session-alist)
+ (current-buffer))))
+ (push new-session crdt--session-list)
new-session))
(defun crdt-stop-session (&optional session-name)
"Stop sharing the current session."
(interactive
(list (completing-read "Choose a session (create if not exist): "
- crdt--session-alist nil t
- (when crdt--status-buffer
- (car (rassq (crdt--network-process)
crdt--session-alist))))))
- (let ((session (if session-name
- (cdr (assoc session-name crdt--session-alist))
- (crdt--network-process))))
- (let ((status-buffer
- (process-get session 'status-buffer)))
- (with-current-buffer status-buffer
- (dolist (client crdt--network-clients)
- (when (process-live-p client)
- (delete-process client))
- (when (process-buffer client)
- (kill-buffer (process-buffer client))))
- (when crdt--user-menu-buffer
- (kill-buffer crdt--user-menu-buffer))
- (maphash
- (lambda (k v)
- (with-current-buffer v
- (setq crdt--status-buffer nil)
- (crdt-mode 0)))
- crdt--buffer-table)
- (setq crdt--session-alist
- (delq (cl-find-if (lambda (p) (eq (cdr p) crdt--network-process))
- crdt--session-alist)
- crdt--session-alist))
- (crdt--refresh-sessions-maybe)
- (delete-process crdt--network-process)
- (message "Disconnected."))
- (kill-buffer status-buffer))))
+ (crdt--get-session-names) nil t
+ (when crdt--status-buffer (crdt--session-name)))))
+ (let ((status-buffer (if session-name
+ (crdt--get-session session-name)
+ (crdt--network-process))))
+ (with-current-buffer status-buffer
+ (dolist (client crdt--network-clients)
+ (when (process-live-p client)
+ (delete-process client))
+ (when (process-buffer client)
+ (kill-buffer (process-buffer client))))
+ (when crdt--user-menu-buffer
+ (kill-buffer crdt--user-menu-buffer))
+ (maphash
+ (lambda (k v)
+ (with-current-buffer v
+ (setq crdt--status-buffer nil)
+ (crdt-mode 0)))
+ crdt--buffer-table)
+ (setq crdt--session-list
+ (delq status-buffer crdt--session-list))
+ (crdt--refresh-sessions-maybe)
+ (delete-process crdt--network-process)
+ (message "Disconnected."))
+ (kill-buffer status-buffer)))
(defun crdt-connect (address port &optional name)
"Connect to a CRDT server running at ADDRESS:PORT.
@@ -1396,7 +1340,8 @@ Open a new buffer to display the shared content."
:plist `(status-buffer ,(current-buffer))))
(t (kill-buffer (current-buffer))
(signal (car err) (cdr err))))
- (push (cons address crdt--network-process) crdt--session-alist)
+ (setq crdt--session-name (format "%s:%s" address port))
+ (push (current-buffer) crdt--session-list)
(setq crdt--local-clock 0)
(process-send-string crdt--network-process
(crdt--format-message `(hello ,name)))
- [elpa] externals/crdt a157310 12/80: better formatting, (continued)
- [elpa] externals/crdt a157310 12/80: better formatting, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 5ec25f7 20/80: input method seems to work now, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt ea632a3 26/80: some docstring and unused variable, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 68d3067 18/80: bug fix, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt d9d7100 53/80: remote process support, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 9a7ce57 56/80: fix fill-paragraph bug, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt bc98495 60/80: I imagine this fix an imaginary bug with tuntox, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt eb184d8 61/80: clean up *crdt - client*, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 76da6ac 62/80: update buffer/user menu in post-command-hook to account for focus change, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 20ff5b3 65/80: reenable crdt-mode and synchronize after major mode change, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt d20b384 16/80: document and improvements,
ELPA Syncer <=
- [elpa] externals/crdt 9c37d4d 19/80: remove superfluous crdt--inhibit-update bind, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 515959c 23/80: partially fixed deleted buffer bug, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 35d9612 28/80: fix some read functions, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt d10f9c0 30/80: bug fix, debug switch, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 2cee2da 41/80: fix bug in crdt-process-message (head remove), ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 12d9a25 40/80: remove buffer bug, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 903ebf9 36/80: better display for address in user menu, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt e7a3607 45/80: fix cl-block, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt c10b38e 43/80: shorten string when ask for stop session confirmation, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt c495170 09/80: remove debug statements, ELPA Syncer, 2021/08/28