[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/crdt 692d2cc 24/80: initial work for lazily pulling buf
From: |
ELPA Syncer |
Subject: |
[elpa] externals/crdt 692d2cc 24/80: initial work for lazily pulling buffer |
Date: |
Sat, 28 Aug 2021 10:57:34 -0400 (EDT) |
branch: externals/crdt
commit 692d2cc4dba825429ce31158cc5219b58d25f6da
Author: Qiantan Hong <qhong@mit.edu>
Commit: Qiantan Hong <qhong@mit.edu>
initial work for lazily pulling buffer
---
HACKING.org | 14 ++-
crdt.el | 327 ++++++++++++++++++++++++++++++++++++------------------------
2 files changed, 206 insertions(+), 135 deletions(-)
diff --git a/HACKING.org b/HACKING.org
index 374f5ad..59c3c04 100644
--- a/HACKING.org
+++ b/HACKING.org
@@ -50,7 +50,7 @@ be invoked with no argument in any CRDT shared buffer to
access or modify that s
- cursor ::
body takes the form
- =(site-id point-position-hint point-crdt-id mark-position-hint
mark-crdt-id)=
+ =(buffer-name 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)=
@@ -85,11 +85,15 @@ be invoked with no argument in any CRDT shared buffer to
access or modify that s
- =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)=
+ - add ::
+ Indicates that the server has started sharing some buffers.
+ body takes the form =buffer-name-list=
+
+ - remove ::
+ Indicates that the server has stopped sharing some buffers.
+ body takes the form =buffer-name-list=
- - resync ::
+ - get ::
(TODO) Request the server to resend =sync= message for a buffer.
body takes the form =(buffer-name)=
diff --git a/crdt.el b/crdt.el
index 95bc576..5f6d46e 100644
--- a/crdt.el
+++ b/crdt.el
@@ -1,4 +1,4 @@
-;;; crdt.el --- collaborative editing using Conflict-free Replicated Data Types
+;;; crdt.el --- collaborative editing using Conflict-free Replicated Data
Types -*- lexical-binding: t; -*-
;;
;; Copyright (C) 2020 Qiantan Hong
;;
@@ -298,8 +298,6 @@ to avoid recusive calling of CRDT synchronization
functions.")
(crdt--defvar-session crdt--contact-table nil
"A hash table that maps SITE-ID to
CRDT--CONTACT-METADATAs.")
-(defvar-local crdt--active-users-tmp nil)
-
(cl-defstruct (crdt--overlay-metadata
(:constructor crdt--make-overlay-metadata
(lamport-timestamp species front-advance
rear-advance plist))
@@ -316,6 +314,8 @@ to avoid recusive calling of CRDT synchronization
functions.")
(crdt--defvar-permanent-local crdt--buffer-network-name)
+(crdt--defvar-permanent-local crdt--buffer-sync-callback)
+
(crdt--defvar-session crdt--local-name nil)
(crdt--defvar-session crdt--session-name nil)
@@ -340,15 +340,29 @@ to avoid recusive calling of CRDT synchronization
functions.")
;;; crdt-mode
+(defun crdt--kill-buffer-hook ()
+ (when crdt--buffer-network-name
+ (puthash crdt--buffer-network-name nil (crdt--buffer-table))
+ (crdt--broadcast-maybe (crdt--format-message
+ `(cursor ,crdt--buffer-network-name
+ ,(crdt--local-id) nil nil nil nil)))
+ (when (eq (crdt--focused-buffer-name) crdt--buffer-network-name)
+ (crdt--broadcast-maybe (crdt--format-message
+ `(focus ,(crdt--local-id) nil)))
+ (setf (crdt--focused-buffer-name) nil))
+ (crdt--refresh-users-maybe)))
+
(defun crdt--install-hooks ()
(add-hook 'after-change-functions #'crdt--after-change nil t)
(add-hook 'before-change-functions #'crdt--before-change nil t)
- (add-hook 'post-command-hook #'crdt--post-command nil t))
+ (add-hook 'post-command-hook #'crdt--post-command nil t)
+ (add-hook 'kill-buffer-hook #'crdt--kill-buffer-hook nil t))
(defun crdt--uninstall-hooks ()
(remove-hook 'after-change-functions #'crdt--after-change t)
(remove-hook 'before-change-functions #'crdt--before-change t)
- (remove-hook 'post-command-hook #'crdt--post-command t))
+ (remove-hook 'post-command-hook #'crdt--post-command t)
+ (remove-hook 'kill-buffer-hook #'crdt--kill-buffer-hook t))
(defsubst crdt--clear-pseudo-cursor-table ()
(when crdt--pseudo-cursor-table
@@ -369,6 +383,46 @@ to avoid recusive calling of CRDT synchronization
functions.")
(crdt--clear-pseudo-cursor-table)
(setq crdt--overlay-table nil)))
+;;; Shared buffer utils
+(defun crdt--with-buffer-name-make-body (name body fallback)
+ `(let (crdt-buffer)
+ (setq crdt-buffer (gethash ,name crdt--buffer-table))
+ (if (and crdt-buffer (buffer-live-p crdt-buffer))
+ (with-current-buffer crdt-buffer
+ ,@body)
+ (unless (process-contact crdt--network-process :server)
+ (setq crdt-buffer (generate-new-buffer (format "crdt - %s" ,name)))
+ (puthash ,name crdt-buffer crdt--buffer-table)
+ (let ((status-buffer (current-buffer)))
+ (with-current-buffer crdt-buffer
+ (setq crdt--buffer-network-name ,name)
+ (setq crdt--status-buffer status-buffer)
+ (crdt-mode)
+ ,(funcall fallback name body)))))))
+
+(defmacro crdt--with-buffer-name (name &rest body)
+ "Find CRDT shared buffer associated with NAME and evaluate BODY in it.
+Must be called when CURRENT-BUFFER is a CRDT status buffer.
+If such buffer doesn't exist yet, do nothing."
+ `(let (crdt-buffer)
+ (setq crdt-buffer (gethash ,name crdt--buffer-table))
+ (when (and crdt-buffer (buffer-live-p crdt-buffer))
+ (with-current-buffer crdt-buffer
+ (save-excursion
+ (widen)
+ ,@body)))))
+
+(defmacro crdt--with-buffer-name-pull (name &rest body)
+ (crdt--with-buffer-name-make-body
+ name body
+ (lambda (name body)
+ `(progn
+ (crdt--broadcast-maybe (crdt--format-message `(get ,,name)))
+ (let ((crdt--inhibit-update t))
+ (insert "Synchronizing with server..."))
+ (setq crdt--buffer-sync-callback
+ (lambda ()
+ ,@body))))))
;;; Session menu
(defun crdt--session-menu-goto ()
@@ -443,12 +497,19 @@ Otherwise use a dedicated buffer for displaying active
users on CRDT-BUFFER."
;;; Buffer menu
(defun crdt--buffer-menu-goto ()
(interactive)
- (switch-to-buffer-other-window (tabulated-list-get-id)))
+ (let ((name (tabulated-list-get-id)))
+ (with-current-buffer crdt--status-buffer
+ (crdt--with-buffer-name-pull name
+ (switch-to-buffer-other-window (current-buffer))))))
(defun crdt--buffer-menu-kill ()
(interactive)
- (with-current-buffer (tabulated-list-get-id)
- (crdt-stop-share-buffer)))
+ (if (crdt--server-p)
+ (let ((name (tabulated-list-get-id)))
+ (with-current-buffer crdt--status-buffer
+ (crdt--with-buffer-name name
+ (crdt-stop-share-buffer))))
+ (message "Only server can stop sharing a buffer.")))
(defvar crdt-buffer-menu-mode-map
(let ((map (make-sparse-keymap)))
@@ -458,7 +519,7 @@ Otherwise use a dedicated buffer for displaying active
users on CRDT-BUFFER."
(define-derived-mode crdt-buffer-menu-mode tabulated-list-mode
"CRDT User List"
- (setq tabulated-list-format [("Buffer" 15 t)
+ (setq tabulated-list-format [("Local Buffer" 15 t)
("Network Name" 15 t)
("Users" 15 t)]))
@@ -493,25 +554,22 @@ Otherwise use a dedicated buffer for displaying active
users on CRDT-BUFFER."
(with-current-buffer display-buffer
(crdt-buffer-menu-mode)
(setq tabulated-list-entries nil)
- (maphash (lambda (k v)
- (crdt--with-current-buffer
- (gethash (crdt--contact-metadata-focused-buffer-name v)
- (crdt--buffer-table))
- (push (crdt--contact-metadata-display-name v)
crdt--active-users-tmp)))
- (crdt--contact-table))
- (crdt--with-current-buffer
- (gethash (crdt--focused-buffer-name) (crdt--buffer-table))
- (push (crdt--local-name)
- crdt--active-users-tmp))
- (maphash (lambda (k v)
- (push (list v (vector (buffer-name v) k
- (mapconcat #'identity
- (with-current-buffer v
crdt--active-users-tmp)
- ", ")))
- tabulated-list-entries)
- (with-current-buffer v
- (setq crdt--active-users-tmp nil)))
- (crdt--buffer-table))
+ (let ((tmp-hashtable (make-hash-table :test 'equal)))
+ (maphash (lambda (k v)
+ (push (crdt--contact-metadata-display-name v)
+ (gethash (crdt--contact-metadata-focused-buffer-name v)
+ tmp-hashtable)))
+ (crdt--contact-table))
+ (push (crdt--local-name)
+ (gethash (crdt--focused-buffer-name)
+ tmp-hashtable))
+ (maphash (lambda (k v)
+ (push (list k (vector (if (and v (buffer-live-p v))
+ (buffer-name v)
+ "--")
+ k (mapconcat #'identity (gethash k
tmp-hashtable) ", ")))
+ tabulated-list-entries))
+ (crdt--buffer-table)))
(tabulated-list-init-header)
(tabulated-list-print)))
@@ -520,20 +578,24 @@ Otherwise use a dedicated buffer for displaying active
users on CRDT-BUFFER."
(crdt-refresh-buffers (crdt--buffer-menu-buffer)))
(crdt--refresh-sessions-maybe))
-
;;; User menu
(defun crdt--user-menu-goto ()
(interactive)
- (let* ((site-id (tabulated-list-get-id))
- (focused-buffer
- (with-current-buffer crdt--status-buffer
- (gethash
- (crdt--contact-metadata-focused-buffer-name
- (gethash site-id crdt--contact-table))
- crdt--buffer-table))))
- (switch-to-buffer-other-window focused-buffer)
- (when site-id
- (goto-char (overlay-start (car (gethash site-id
crdt--pseudo-cursor-table)))))))
+ (let ((site-id (tabulated-list-get-id)))
+ (if (eq site-id (crdt--local-id))
+ (switch-to-buffer-other-window
+ (gethash (crdt--focused-buffer-name) (crdt--buffer-table)))
+ (unless
+ (cl-block nil
+ (let* ((metadata (or (gethash site-id (crdt--contact-table))
(cl-return)))
+ (buffer-name (or
(crdt--contact-metadata-focused-buffer-name metadata) (cl-return))))
+ (with-current-buffer crdt--status-buffer
+ (crdt--with-buffer-name-pull
+ buffer-name
+ (switch-to-buffer-other-window (current-buffer))
+ (ignore-errors (goto-char (overlay-start (car (gethash
site-id crdt--pseudo-cursor-table)))))
+ t))))
+ (message "Doesn't have position information for this user yet.")))))
(defvar crdt-user-menu-mode-map
(let ((map (make-sparse-keymap)))
@@ -790,7 +852,7 @@ Start the search from POS."
(point-max)))
(defun crdt--remote-cursor (site-id point-position-hint point-crdt-id
mark-position-hint mark-crdt-id)
- (when site-id
+ (when (and site-id (not (eq site-id (crdt--local-id))))
(let ((ov-pair (gethash site-id crdt--pseudo-cursor-table)))
(if point-crdt-id
(let* ((point (crdt--id-to-pos point-crdt-id point-position-hint))
@@ -955,7 +1017,27 @@ to server when WITHOUT is T."
`(cursor ,crdt--buffer-network-name
,site-id
,point ,point-id-base64
,mark ,mark-id-base64))))))
crdt--pseudo-cursor-table)
- (process-send-string process (crdt--format-message (crdt--local-cursor
nil)))))
+ (process-send-string process (crdt--format-message (crdt--local-cursor
nil)))
+
+ ;; synchronize tracked overlay
+ (maphash (lambda (k ov)
+ (let ((meta (overlay-get ov 'crdt-meta)))
+ (process-send-string
+ process
+ (crdt--format-message (crdt--overlay-add-message
+ (car k) (cdr k)
+ (crdt--overlay-metadata-species meta)
+ (crdt--overlay-metadata-front-advance
meta)
+ (crdt--overlay-metadata-rear-advance
meta)
+ (overlay-start ov)
+ (overlay-end ov))))
+ (cl-loop for (prop value) on (crdt--overlay-metadata-plist
meta) by #'cddr
+ do (process-send-string
+ process
+ (crdt--format-message `(overlay-put ,(car k) ,(cdr
k) ,prop ,value))))))
+ crdt--overlay-table)
+
+ (process-send-string process (crdt--format-message `(ready
,crdt--buffer-network-name)))))
(defun crdt--greet-client (process)
(with-current-buffer (process-get process 'status-buffer)
@@ -970,9 +1052,8 @@ to server when WITHOUT is T."
`(login ,client-id
,crdt--session-name)))
(cl-incf crdt--next-client-id))
- (maphash (lambda (k buffer)
- (crdt--sync-buffer-to-client buffer process))
- crdt--buffer-table)
+ (process-send-string process (crdt--format-message
+ (cons 'add (hash-table-keys
crdt--buffer-table))))
;; synchronize contact
(maphash (lambda (k v)
(process-send-string
@@ -991,48 +1072,7 @@ to server when WITHOUT is T."
(let ((contact-message `(contact ,client-id ,(process-get process
'client-name)
,(process-contact process :host)
,(process-contact process :service))))
- (crdt-process-message contact-message process))
- ;; synchronize tracked overlay
- (maphash (lambda (k buffer)
- (with-current-buffer buffer
- (maphash (lambda (k ov)
- (let ((meta (overlay-get ov 'crdt-meta)))
- (process-send-string
- process
- (crdt--format-message
(crdt--overlay-add-message
- (car k) (cdr k)
-
(crdt--overlay-metadata-species meta)
-
(crdt--overlay-metadata-front-advance meta)
-
(crdt--overlay-metadata-rear-advance meta)
- (overlay-start ov)
- (overlay-end ov))))
- (cl-loop for (prop value) on
(crdt--overlay-metadata-plist meta) by #'cddr
- do (process-send-string
- process
- (crdt--format-message `(overlay-put
,(car k) ,(cdr k) ,prop ,value))))))
- crdt--overlay-table)))
- crdt--buffer-table))))
-
-(defmacro crdt--with-buffer-name (name &rest body)
- "Find CRDT shared buffer associated with NAME and evaluate BODY in it.
-Must be called when CURRENT-BUFFER is a CRDT status buffer."
- `(let (crdt-buffer)
- (setq crdt-buffer (gethash ,name crdt--buffer-table))
- (if crdt-buffer
- (with-current-buffer crdt-buffer
- (save-excursion
- (widen)
- ,@body))
- (unless (process-contact crdt--network-process :server)
- (setq crdt-buffer (generate-new-buffer (format "crdt - %s" ,name)))
- (puthash ,name crdt-buffer crdt--buffer-table)
- (with-current-buffer crdt-buffer
- (setq crdt--buffer-network-name ,name)
- (setq crdt--status-buffer (process-get process 'status-buffer))
- (crdt-mode)
- (save-excursion
- (widen)
- ,@body))))))
+ (crdt-process-message contact-message process)))))
(cl-defgeneric crdt-process-message (message process))
@@ -1070,29 +1110,53 @@ Must be called when CURRENT-BUFFER is a CRDT status
buffer."
(defsubst crdt--server-p ()
(process-contact (crdt--network-process) :server))
+(cl-defmethod crdt-process-message ((message (head get)) process)
+ (cl-destructuring-bind (buffer-name) (cdr message)
+ (let ((buffer (gethash buffer-name crdt--buffer-table)))
+ (if (and buffer (buffer-live-p buffer))
+ (crdt--sync-buffer-to-client buffer process)
+ (process-send-string process (crdt--format-message `(desync
,buffer-name)))))))
+
(cl-defmethod crdt-process-message ((message (head sync)) process)
(unless (crdt--server-p) ; server shouldn't receive this
(cl-destructuring-bind (buffer-name mode . ids) (cdr message)
(crdt--with-buffer-name
buffer-name
- (erase-buffer)
- (if (fboundp mode)
- (unless (eq major-mode mode)
- (funcall mode) ; trust your server...
- (crdt-mode))
- (message "Server uses %s, but not available locally." mode))
- (crdt--load-ids ids)))
+ (let ((crdt--inhibit-update t))
+ (erase-buffer)
+ (if (fboundp mode)
+ (unless (eq major-mode mode)
+ (funcall mode) ; trust your server...
+ (crdt-mode))
+ (message "Server uses %s, but not available locally." mode))
+ (crdt--load-ids ids))))
(crdt--refresh-buffers-maybe)))
-(cl-defmethod crdt-process-message ((message (head desync)) process)
- (cl-destructuring-bind (buffer-name) (cdr message)
+(cl-defmethod crdt-process-message ((message (head ready)) process)
+ (unless (crdt--server-p) ; server shouldn't receive this
+ (cl-destructuring-bind (buffer-name) (cdr message)
+ (crdt--with-buffer-name
+ buffer-name
+ (when crdt--buffer-sync-callback
+ (funcall crdt--buffer-sync-callback)
+ (setq crdt--buffer-sync-callback nil))))))
+
+(cl-defmethod crdt-process-message ((message (head add)) process)
+ (dolist (buffer-name (cdr message))
+ (unless (gethash buffer-name crdt--buffer-table)
+ (puthash buffer-name nil crdt--buffer-table))))
+
+(cl-defmethod crdt-process-message ((message (head remove)) process)
+ (dolist (buffer-name (cdr message))
(let ((buffer (gethash buffer-name crdt--buffer-table)))
(when buffer
- (with-current-buffer buffer
- (crdt-mode 0)
- (setq crdt--status-buffer nil))
- (remhash buffer-name crdt--buffer-table)
- (message "Server stopped sharing %s." buffer-name))))
+ (when (buffer-live-p buffer)
+ (with-current-buffer buffer
+ (crdt-mode 0)
+ (setq crdt--status-buffer nil)))
+ (remhash buffer-name crdt--buffer-table))))
+ (message "Server stopped sharing %s."
+ (mapconcat #'identity (cdr message) ", "))
(crdt--broadcast-maybe (crdt--format-message message)
(when process (process-get process 'client-id)))
(crdt--refresh-buffers-maybe))
@@ -1136,9 +1200,9 @@ Must be called when CURRENT-BUFFER is a CRDT status
buffer."
(site-id buffer-name) (cdr message)
(let ((existing-item (gethash site-id crdt--contact-table)))
(setf (crdt--contact-metadata-focused-buffer-name existing-item)
buffer-name))
- (when (and (= site-id 0) (not crdt--focused-buffer-name))
- (setq crdt--focused-buffer-name buffer-name)
- (switch-to-buffer (gethash buffer-name crdt--buffer-table)))
+ ;; (when (and (= site-id 0) (not crdt--focused-buffer-name))
+ ;; (setq crdt--focused-buffer-name buffer-name)
+ ;; (switch-to-buffer (gethash buffer-name crdt--buffer-table)))
(crdt--refresh-users-maybe))
(crdt--broadcast-maybe (crdt--format-message message) (process-get process
'client-id)))
@@ -1282,8 +1346,8 @@ If SESSION-NAME is empty, use the buffer name of the
current buffer."
(if (crdt--server-p)
(let ((buffer-name crdt--buffer-network-name))
(with-current-buffer crdt--status-buffer
- (let ((desync-message `(desync ,buffer-name)))
- (crdt-process-message desync-message nil))))
+ (let ((remove-message `(remove ,buffer-name)))
+ (crdt-process-message remove-message nil))))
(message "Only server can stop sharing a buffer."))
(message "Not a CRDT shared buffer.")))
@@ -1342,9 +1406,11 @@ Disconnect if it's a client session, or stop serving if
it's a server session."
(kill-buffer (process-buffer client))))
(when crdt--user-menu-buffer
(kill-buffer crdt--user-menu-buffer))
+ (when crdt--buffer-menu-buffer
+ (kill-buffer crdt--buffer-menu-buffer))
(maphash
(lambda (k v)
- (when (buffer-live-p v)
+ (when (and v (buffer-live-p v))
(with-current-buffer v
(setq crdt--status-buffer nil)
(crdt-mode 0))))
@@ -1391,30 +1457,31 @@ Open a new buffer to display the shared content."
port)))
(unless name
(setq name (crdt--read-name)))
- (setq crdt--status-buffer
- (with-current-buffer (generate-new-buffer "*crdt-client*")
- (setq crdt--local-name name)
- (condition-case err
- (setq crdt--network-process
- (make-network-process
- :name "CRDT Client"
- :buffer (current-buffer)
- :host address
- :family 'ipv4
- :service port
- :filter #'crdt--network-filter
- :sentinel #'crdt--client-process-sentinel
- :plist `(status-buffer ,(current-buffer))))
- (t (kill-buffer (current-buffer))
- (signal (car err) (cdr err))))
- (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)))
- (setq crdt--contact-table (make-hash-table :test 'equal))
- (setq crdt--buffer-table (make-hash-table :test 'equal))
- (setq crdt--status-buffer (current-buffer)))))
+ (crdt-list-buffer (with-current-buffer
+ (with-current-buffer (generate-new-buffer
"*crdt-client*")
+ (setq crdt--local-name name)
+ (condition-case err
+ (setq crdt--network-process
+ (make-network-process
+ :name "CRDT Client"
+ :buffer (current-buffer)
+ :host address
+ :family 'ipv4
+ :service port
+ :filter #'crdt--network-filter
+ :sentinel #'crdt--client-process-sentinel
+ :plist `(status-buffer
,(current-buffer))))
+ (t (kill-buffer (current-buffer))
+ (signal (car err) (cdr err))))
+ (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)))
+ (setq crdt--contact-table (make-hash-table :test
'equal))
+ (setq crdt--buffer-table (make-hash-table :test
'equal))
+ (setq crdt--status-buffer (current-buffer)))
+ )))
(defun crdt-test-client ()
(interactive)
- [elpa] branch externals/crdt created (now 8cbd0fd), ELPA Syncer, 2021/08/28
- [elpa] externals/crdt f224e8a 06/80: add doc. add error handler in network filter, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 22910f2 01/80: initial commit, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 7ebbd5e 05/80: cleanup debug code, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 21f62ad 14/80: various fixes, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 958394a 08/80: doc, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 6b320c7 11/80: bug fix, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt bfce95c 17/80: formatting, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 692d2cc 24/80: initial work for lazily pulling buffer,
ELPA Syncer <=
- [elpa] externals/crdt b8cd461 22/80: Minor improvements, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 8307092 29/80: better read function, tab width wider, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 4c7e12d 37/80: documentation, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 53e5676 33/80: fix bug with org table, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt fcc6a47 38/80: fix upper/lowercase convention, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt e1b0fe9 02/80: Fixed silly typo!, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 88cd9a1 03/80: refactorz, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 432b5f8 04/80: two bug fixes for CRDT algorithm, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt 82a7565 10/80: fix authentication bug, ELPA Syncer, 2021/08/28
- [elpa] externals/crdt b95111c 15/80: add makefile, ELPA Syncer, 2021/08/28