update packages
This commit is contained in:
315
lisp/crdt.el
315
lisp/crdt.el
@@ -44,8 +44,8 @@
|
||||
"Ask for server password everytime a CRDT server is to be started."
|
||||
:type 'boolean)
|
||||
|
||||
(defcustom crdt-confirm-stop-session t
|
||||
"Ask for confirmation when a CRDT server with some client connected is to be stopped."
|
||||
(defcustom crdt-confirm-disconnect t
|
||||
"Ask for confirmation when a CRDT server is to stop the connection from some client."
|
||||
:type 'boolean)
|
||||
|
||||
(defvar crdt--log-network-traffic nil
|
||||
@@ -191,7 +191,7 @@ and HIGH-OFFSET. (to save two copying from using CRDT--ID-REPLACE-OFFSET)"
|
||||
(get-text-property pos 'crdt-id obj))
|
||||
|
||||
(defsubst crdt--get-starting-id (pos &optional obj)
|
||||
"Get the CRDT-ID at POS in OBJ."
|
||||
"Get the CRDT-ID object at POS in OBJ."
|
||||
(car (crdt--get-crdt-id-pair pos obj)))
|
||||
|
||||
(defsubst crdt--end-of-block-p (pos &optional obj)
|
||||
@@ -295,12 +295,19 @@ Must be used inside CRDT--WITH-INSERTION-INFORMATION."
|
||||
This is useful for functions that apply remote change to local buffer,
|
||||
to avoid recusive calling of CRDT synchronization functions.")
|
||||
|
||||
(crdt--defvar-permanent-local crdt--changed-string nil)
|
||||
(crdt--defvar-permanent-local crdt--changed-string nil
|
||||
"Save changed substring in CRDT--BEFORE-CHANGE.")
|
||||
|
||||
(crdt--defvar-permanent-local crdt--changed-start nil
|
||||
"Save start character address of changes in CRDT--BEFORE-CHANGE,
|
||||
to recover the portion being overwritten in CRDT--AFTER-CHANGE.")
|
||||
|
||||
(crdt--defvar-permanent-local crdt--last-point nil)
|
||||
|
||||
(crdt--defvar-permanent-local crdt--last-mark nil)
|
||||
|
||||
(crdt--defvar-permanent-local crdt--last-process-mark-id nil)
|
||||
|
||||
(crdt--defvar-permanent-local crdt--pseudo-cursor-table nil
|
||||
"A hash table that maps SITE-ID to CONSes of the form (CURSOR-OVERLAY . REGION-OVERLAY).")
|
||||
|
||||
@@ -326,6 +333,8 @@ to avoid recusive calling of CRDT synchronization functions.")
|
||||
|
||||
(crdt--defvar-permanent-local crdt--buffer-sync-callback)
|
||||
|
||||
(crdt--defvar-permanent-local crdt--buffer-pseudo-process)
|
||||
|
||||
;;; Global variables
|
||||
|
||||
(defvar crdt--session-list nil)
|
||||
@@ -364,8 +373,10 @@ Also set CRDT--PSEUDO-CURSOR-TABLE to NIL."
|
||||
"CRDT mode" nil " CRDT" nil
|
||||
(if crdt-mode
|
||||
(progn
|
||||
(setq crdt--pseudo-cursor-table (make-hash-table))
|
||||
(setq crdt--overlay-table (make-hash-table :test 'equal))
|
||||
(unless crdt--pseudo-cursor-table
|
||||
(setq crdt--pseudo-cursor-table (make-hash-table)))
|
||||
(unless crdt--overlay-table
|
||||
(setq crdt--overlay-table (make-hash-table :test 'equal)))
|
||||
(crdt--install-hooks))
|
||||
(crdt--uninstall-hooks)
|
||||
(crdt--clear-pseudo-cursor-table)
|
||||
@@ -383,13 +394,18 @@ If SESSION is nil, use current CRDT--SESSION."
|
||||
|
||||
(defmacro crdt--with-buffer-name (name &rest body)
|
||||
"Find CRDT shared buffer associated with NAME and evaluate BODY in it.
|
||||
Also, try to recover from synchronization error if any error happens in BODY.
|
||||
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--session-buffer-table crdt--session)))
|
||||
(when (and crdt-buffer (buffer-live-p crdt-buffer))
|
||||
(with-current-buffer crdt-buffer
|
||||
,@body))))
|
||||
(condition-case err
|
||||
,(cons 'progn body)
|
||||
(error (if (crdt--server-p)
|
||||
(signal (car err) (cdr err)) ; didn't implement server side recovery yet
|
||||
(crdt--client-recover))))))))
|
||||
|
||||
(defmacro crdt--with-buffer-name-pull (name &rest body)
|
||||
"Find CRDT shared buffer associated with NAME and evaluate BODY in it.
|
||||
@@ -412,7 +428,8 @@ after synchronization is completed."
|
||||
(crdt-mode)
|
||||
(crdt--broadcast-maybe (crdt--format-message `(get ,,name)))
|
||||
(let ((crdt--inhibit-update t))
|
||||
(insert "Synchronizing with server..."))
|
||||
(insert "Synchronizing with server...")
|
||||
(read-only-mode))
|
||||
(setq crdt--buffer-sync-callback
|
||||
(lambda ()
|
||||
,@body))))))))
|
||||
@@ -587,9 +604,26 @@ Otherwise use a dedicated buffer for displaying active users on CRDT-BUFFER."
|
||||
t)))
|
||||
(message "Doesn't have position information for this user yet.")))))
|
||||
|
||||
(defun crdt--user-menu-kill ()
|
||||
"Disconnect the user under point in CRDT user menu.
|
||||
Only server can perform this action."
|
||||
(interactive)
|
||||
(if (crdt--server-p)
|
||||
(let ((site-id (tabulated-list-get-id)))
|
||||
(if site-id
|
||||
(if (eq site-id (crdt--session-local-id crdt--session))
|
||||
(message "Suicide is not allowed.")
|
||||
(dolist (p (process-list))
|
||||
(when (eq (process-get p 'client-id) site-id)
|
||||
(delete-process p))))
|
||||
(message "We somehow don't have the SITE-ID for this user.
|
||||
Please submit a bug report to crdt.el maintainer.")))
|
||||
(message "Only server can disconnect a user.")))
|
||||
|
||||
(defvar crdt-user-menu-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "RET") #'crdt--user-menu-goto)
|
||||
(define-key map (kbd "k") #'crdt--user-menu-kill)
|
||||
map))
|
||||
|
||||
(define-derived-mode crdt-user-menu-mode tabulated-list-mode
|
||||
@@ -599,9 +633,10 @@ Otherwise use a dedicated buffer for displaying active users on CRDT-BUFFER."
|
||||
("Address" 15 t)]))
|
||||
|
||||
(defun crdt-list-users (&optional crdt-buffer display-buffer)
|
||||
"Display a list of active users working on a CRDT-shared buffer CRDT-BUFFER.
|
||||
"Display a list of active users working on a CRDT-shared session.
|
||||
Find the session in CRDT-BUFFER if non NIL, or current buffer.
|
||||
If DISPLAY-BUFFER is provided, display the output there.
|
||||
Otherwise use a dedicated buffer for displaying active users on CRDT-BUFFER."
|
||||
Otherwise create a dedicated buffer."
|
||||
(interactive)
|
||||
(with-current-buffer (or crdt-buffer (current-buffer))
|
||||
(unless crdt--session
|
||||
@@ -787,10 +822,11 @@ Start the search around POSITION-HINT."
|
||||
;; (crdt--verify-buffer)
|
||||
)
|
||||
|
||||
(defun crdt--local-delete (beg end)
|
||||
(defun crdt--local-delete (beg end length)
|
||||
"Handle local deletion event and return a message to be sent to other peers.
|
||||
The deletion happens between BEG and END."
|
||||
(let ((outer-end end))
|
||||
The deletion happens between BEG and END, and have LENGTH."
|
||||
(let ((outer-end end)
|
||||
(crdt--changed-string (crdt--changed-string beg length)))
|
||||
(crdt--with-insertion-information
|
||||
(beg 0 nil crdt--changed-string nil (length crdt--changed-string))
|
||||
(when (crdt--split-maybe)
|
||||
@@ -801,10 +837,10 @@ The deletion happens between BEG and END."
|
||||
(crdt--id-replace-offset ending-id (+ 1 left-offset (length crdt--changed-string))))))))
|
||||
(crdt--with-insertion-information
|
||||
((length crdt--changed-string) outer-end crdt--changed-string nil 0 nil)
|
||||
(crdt--split-maybe)))
|
||||
;; (crdt--verify-buffer)
|
||||
`(delete ,crdt--buffer-network-name
|
||||
,beg ,@ (crdt--dump-ids 0 (length crdt--changed-string) crdt--changed-string t)))
|
||||
(crdt--split-maybe))
|
||||
;; (crdt--verify-buffer)
|
||||
`(delete ,crdt--buffer-network-name
|
||||
,beg ,@ (crdt--dump-ids 0 (length crdt--changed-string) crdt--changed-string t))))
|
||||
|
||||
(defun crdt--remote-delete (position-hint id-items)
|
||||
"Handle remote deletion message of ID-ITEMS.
|
||||
@@ -837,7 +873,13 @@ Start the search for those ID-ITEMs around POSITION-HINT."
|
||||
"Before change hook used by CRDT-MODE.
|
||||
It saves the content to be changed (between BEG and END) into CRDT--CHANGED-STRING."
|
||||
(unless crdt--inhibit-update
|
||||
(setq crdt--changed-string (buffer-substring beg end))))
|
||||
(setq crdt--changed-string (buffer-substring beg end))
|
||||
(setq crdt--changed-start beg)))
|
||||
|
||||
(defsubst crdt--changed-string (beg length)
|
||||
"Retrieve part of CRDT--CHANGED-STRING starting at BEG with LENGTH before change."
|
||||
(let ((from (- beg crdt--changed-start)))
|
||||
(substring crdt--changed-string from (+ from length))))
|
||||
|
||||
(defsubst crdt--crdt-id-assimilate (template beg &optional object)
|
||||
"Make the CRDT-ID property after BEG in OBJECT the same as TEMPLATE.
|
||||
@@ -854,11 +896,9 @@ TEMPLATE should be a string. If OBJECT is NIL, use current buffer."
|
||||
|
||||
(defun crdt--after-change (beg end length)
|
||||
"After change hook used by CRDT-MODE.
|
||||
It examine CRDT--CHANGED-STRING (should be saved by
|
||||
CRDT--BEFORE-STRING and whose length shall equal to LENGTH)
|
||||
and current content between BEG and END,
|
||||
update the CRDT-ID for any newly inserted text,
|
||||
and send message to other peers if needed."
|
||||
It examine (CRDT--CHANGED-STRING) (should be saved by CRDT--BEFORE-STRING)
|
||||
and current content between BEG and END with LENGTH,
|
||||
update the CRDT-ID for any newly inserted text, and send message to other peers if needed."
|
||||
(when (markerp beg)
|
||||
(setq beg (marker-position beg)))
|
||||
(when (markerp end)
|
||||
@@ -875,18 +915,25 @@ and send message to other peers if needed."
|
||||
(save-excursion
|
||||
(goto-char beg)
|
||||
(if (and (= length (- end beg))
|
||||
(string-equal crdt--changed-string
|
||||
(string-equal (crdt--changed-string beg length)
|
||||
(buffer-substring-no-properties beg end)))
|
||||
(crdt--crdt-id-assimilate crdt--changed-string beg)
|
||||
(crdt--crdt-id-assimilate (crdt--changed-string beg length) beg)
|
||||
(widen)
|
||||
(with-silent-modifications
|
||||
(unless (= length 0)
|
||||
(crdt--broadcast-maybe
|
||||
(crdt--format-message (crdt--local-delete beg end))))
|
||||
(crdt--format-message (crdt--local-delete beg end length))))
|
||||
(unless (= beg end)
|
||||
(dolist (message (crdt--local-insert beg end))
|
||||
(crdt--broadcast-maybe
|
||||
(crdt--format-message message)))))))))))
|
||||
(crdt--format-message message)))))))
|
||||
;; process-mark synchronization is dependent on correct CRDT-ID
|
||||
;; therefore we must do it after the insert/change stuff is done
|
||||
(crdt--send-process-mark-maybe)
|
||||
;; see if region stuff changed
|
||||
(let ((cursor-message (crdt--local-cursor)))
|
||||
(when cursor-message
|
||||
(crdt--broadcast-maybe (crdt--format-message cursor-message))))))))
|
||||
|
||||
;;; CRDT point/mark synchronization
|
||||
|
||||
@@ -957,7 +1004,8 @@ Send message to other peers about any changes."
|
||||
(unless (eq crdt--buffer-network-name (crdt--session-focused-buffer-name crdt--session))
|
||||
(crdt--broadcast-maybe
|
||||
(crdt--format-message `(focus ,(crdt--session-local-id crdt--session) ,crdt--buffer-network-name)))
|
||||
(setf (crdt--session-focused-buffer-name crdt--session) crdt--buffer-network-name))
|
||||
(setf (crdt--session-focused-buffer-name crdt--session) crdt--buffer-network-name)
|
||||
(crdt--refresh-users-maybe))
|
||||
(let ((cursor-message (crdt--local-cursor)))
|
||||
(when cursor-message
|
||||
(crdt--broadcast-maybe (crdt--format-message cursor-message)))))
|
||||
@@ -1016,6 +1064,17 @@ Verify that CRDT IDs in a document follows ascending order."
|
||||
(setq pos next-pos)
|
||||
(setq id next-id))))))
|
||||
|
||||
;;; Recovery
|
||||
|
||||
(defun crdt--client-recover ()
|
||||
"Try to recover from a synchronization failure from a client.
|
||||
Current buffer is assmuned to be the one with synchronization error."
|
||||
(ding)
|
||||
(read-only-mode)
|
||||
(message "Synchronization error detected, try recovering...")
|
||||
(crdt--broadcast-maybe
|
||||
(crdt--format-message `(get ,crdt--buffer-network-name))))
|
||||
|
||||
;;; Network protocol
|
||||
|
||||
(defun crdt--format-message (args)
|
||||
@@ -1031,7 +1090,7 @@ If (CRDT--SESSION-NETWORK-PROCESS CRDT--SESSION) is a server process,
|
||||
broadcast MESSAGE-STRING to clients except the one of which CLIENT-ID
|
||||
property is EQ to WITHOUT.
|
||||
If (CRDT--SESSION-NETWORK-PROCESS CRDT--SESSION) is a client process,
|
||||
send MESSAGE-STRING to server when WITHOUT is T."
|
||||
send MESSAGE-STRING to server when WITHOUT is non-nil."
|
||||
(when crdt--log-network-traffic
|
||||
(message "Send %s" message-string))
|
||||
(if (process-contact (crdt--session-network-process crdt--session) :server)
|
||||
@@ -1061,7 +1120,7 @@ The overlay is FRONT-ADVANCE and REAR-ADVANCE, and lies between BEG and END."
|
||||
(base64-encode-string (crdt--get-id end))
|
||||
(crdt--base64-encode-maybe (crdt--get-id (1- end))))))
|
||||
|
||||
(defun crdt--generate-challenge ()
|
||||
(defsubst crdt--generate-challenge ()
|
||||
"Generate a challenge string for authentication."
|
||||
(apply #'unibyte-string (cl-loop for i below 32 collect (random 256))))
|
||||
|
||||
@@ -1069,9 +1128,11 @@ The overlay is FRONT-ADVANCE and REAR-ADVANCE, and lies between BEG and END."
|
||||
"Send messages to a client about the full state of BUFFER.
|
||||
The network process for the client connection is PROCESS."
|
||||
(with-current-buffer buffer
|
||||
(process-send-string process (crdt--format-message `(sync
|
||||
,crdt--buffer-network-name
|
||||
,@ (crdt--dump-ids (point-min) (point-max) nil nil t))))
|
||||
(process-send-string process
|
||||
(crdt--format-message
|
||||
`(sync
|
||||
,crdt--buffer-network-name
|
||||
,@ (crdt--dump-ids (point-min) (point-max) nil nil t))))
|
||||
;; synchronize cursor
|
||||
(maphash (lambda (site-id ov-pair)
|
||||
(cl-destructuring-bind (cursor-ov . region-ov) ov-pair
|
||||
@@ -1109,6 +1170,15 @@ The network process for the client connection is PROCESS."
|
||||
,(car k) ,(cdr k) ,prop ,value))))))
|
||||
crdt--overlay-table)
|
||||
|
||||
;; synchronize process marker if there's any
|
||||
(let ((buffer-process (get-buffer-process buffer)))
|
||||
(when buffer-process
|
||||
(let ((mark-pos (marker-position (process-mark buffer-process))))
|
||||
(process-send-string process
|
||||
(crdt--format-message
|
||||
`(process-mark ,crdt--buffer-network-name
|
||||
,(crdt--get-id mark-pos) ,mark-pos))))))
|
||||
|
||||
(process-send-string process (crdt--format-message `(ready ,crdt--buffer-network-name ,major-mode)))))
|
||||
|
||||
(defun crdt--greet-client (process)
|
||||
@@ -1132,19 +1202,23 @@ The network process for the client connection is PROCESS."
|
||||
(cons 'add (hash-table-keys (crdt--session-buffer-table crdt--session)))))
|
||||
;; synchronize contact
|
||||
(maphash (lambda (k v)
|
||||
(process-send-string
|
||||
process (crdt--format-message `(contact ,k ,(crdt--contact-metadata-display-name v)
|
||||
,(crdt--contact-metadata-host v)
|
||||
,(crdt--contact-metadata-service v))))
|
||||
(process-send-string
|
||||
process (crdt--format-message `(focus ,k ,(crdt--contact-metadata-focused-buffer-name v)))))
|
||||
(process-send-string process
|
||||
(crdt--format-message
|
||||
`(contact ,k ,(crdt--contact-metadata-display-name v)
|
||||
,(crdt--contact-metadata-host v)
|
||||
,(crdt--contact-metadata-service v))))
|
||||
(process-send-string process
|
||||
(crdt--format-message
|
||||
`(focus ,k ,(crdt--contact-metadata-focused-buffer-name v)))))
|
||||
(crdt--session-contact-table crdt--session))
|
||||
(process-send-string process
|
||||
(crdt--format-message `(contact ,(crdt--session-local-id crdt--session)
|
||||
,(crdt--session-local-name crdt--session))))
|
||||
(crdt--format-message
|
||||
`(contact ,(crdt--session-local-id crdt--session)
|
||||
,(crdt--session-local-name crdt--session))))
|
||||
(process-send-string process
|
||||
(crdt--format-message `(focus ,(crdt--session-local-id crdt--session)
|
||||
,(crdt--session-focused-buffer-name crdt--session))))
|
||||
(crdt--format-message
|
||||
`(focus ,(crdt--session-local-id crdt--session)
|
||||
,(crdt--session-focused-buffer-name crdt--session))))
|
||||
(let ((contact-message `(contact ,client-id ,(process-get process 'client-name)
|
||||
,(process-contact process :host)
|
||||
,(process-contact process :service))))
|
||||
@@ -1195,6 +1269,7 @@ The network process for the client connection is PROCESS."
|
||||
(cl-destructuring-bind (buffer-name . ids) (cdr message)
|
||||
(crdt--with-buffer-name
|
||||
buffer-name
|
||||
(read-only-mode -1)
|
||||
(let ((crdt--inhibit-update t))
|
||||
(unless crdt--buffer-sync-callback
|
||||
;; try to get to the same position after sync,
|
||||
@@ -1301,7 +1376,8 @@ Handle received STRING from PROCESS."
|
||||
(unless (and (process-buffer process)
|
||||
(buffer-live-p (process-buffer process)))
|
||||
(set-process-buffer process (generate-new-buffer "*crdt-server*"))
|
||||
(set-marker (process-mark process) 1))
|
||||
(with-current-buffer (process-buffer process)
|
||||
(set-marker (process-mark process) 1)))
|
||||
(with-current-buffer (process-buffer process)
|
||||
(unless crdt--session
|
||||
(setq crdt--session (process-get process 'crdt-session)))
|
||||
@@ -1336,7 +1412,8 @@ Handle received STRING from PROCESS."
|
||||
(error (message "%s error when processing message from %s:%s, disconnecting." err
|
||||
(process-contact process :host) (process-contact process :service))
|
||||
(if (crdt--server-p)
|
||||
(delete-process process)
|
||||
(progn
|
||||
(delete-process process))
|
||||
(crdt--stop-session crdt--session))))))
|
||||
(delete-region (point-min) (point))
|
||||
(goto-char (point-min)))))))
|
||||
@@ -1365,6 +1442,7 @@ Handle received STRING from PROCESS."
|
||||
(unless (eq (process-status process) 'open)
|
||||
(when (process-get process 'tuntox-process)
|
||||
(process-send-string process (crdt--format-message '(leave))))
|
||||
(ding)
|
||||
(crdt--stop-session (process-get process 'crdt-session))))
|
||||
|
||||
;;; UI commands
|
||||
@@ -1417,10 +1495,12 @@ Otherwise, return the list of names for client sessions."
|
||||
(cl-find name crdt--session-list
|
||||
:test 'equal :key #'crdt--session-name))
|
||||
|
||||
(defun crdt-share-buffer (session-name)
|
||||
(defun crdt-share-buffer (session-name &optional port)
|
||||
"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."
|
||||
Create a new one if such a CRDT session doesn't exist. When PORT
|
||||
is non-NIL use when creating a new session, otherwise prompt
|
||||
from minibuffer. If SESSION-NAME is empty, use the buffer name
|
||||
of the current buffer."
|
||||
(interactive
|
||||
(progn
|
||||
(when (and crdt-mode crdt--session)
|
||||
@@ -1438,7 +1518,7 @@ If SESSION-NAME is empty, use the buffer name of the current buffer."
|
||||
(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")))
|
||||
(let ((port (or port (read-from-minibuffer "Create new session on port (default 6530): " nil nil t nil "6530"))))
|
||||
(when (not (numberp port))
|
||||
(error "Port must be a number"))
|
||||
(crdt--share-buffer (current-buffer) (crdt-new-session port session-name))))))
|
||||
@@ -1504,7 +1584,7 @@ Setup up the server with PASSWORD and assign this Emacs DISPLAY-NAME."
|
||||
(defun crdt--stop-session (session)
|
||||
"Kill the CRDT SESSION.
|
||||
Disconnect if it's a client session, or stop serving if it's a server session."
|
||||
(when (if (and crdt-confirm-stop-session
|
||||
(when (if (and crdt-confirm-disconnect
|
||||
(crdt--server-p session)
|
||||
(crdt--session-network-clients session))
|
||||
(yes-or-no-p "There are yet connected clients. Stop session? ")
|
||||
@@ -1528,10 +1608,14 @@ Disconnect if it's a client session, or stop serving if it's a server session."
|
||||
(setq crdt--session-list
|
||||
(delq session crdt--session-list))
|
||||
(crdt--refresh-sessions-maybe)
|
||||
(let ((proxy-process (process-get (crdt--session-network-process session) 'tuntox-process)))
|
||||
(let* ((process (crdt--session-network-process session))
|
||||
(proxy-process (process-get process 'tuntox-process))
|
||||
(process-buffer (process-buffer process)))
|
||||
(delete-process (crdt--session-network-process session))
|
||||
(when (and process-buffer (buffer-live-p process-buffer))
|
||||
(kill-buffer process-buffer))
|
||||
(when (and proxy-process (process-live-p proxy-process))
|
||||
(interrupt-process proxy-process)))
|
||||
(delete-process (crdt--session-network-process session))
|
||||
(message "Disconnected.")))
|
||||
|
||||
(defun crdt-stop-session (&optional session-name)
|
||||
@@ -1761,7 +1845,7 @@ Join with DISPLAY-NAME."
|
||||
(crdt--base64-encode-maybe (crdt--get-id (1- end))))))))))))
|
||||
(apply orig-fun ov beg end args))
|
||||
|
||||
(cl-defmethod crdt-process-message ((message (head overlay-move)) process)
|
||||
(cl-defmethod crdt-process-message ((message (head overlay-move)) _process)
|
||||
(cl-destructuring-bind (buffer-name site-id logical-clock
|
||||
start-hint start-id-base64 end-hint end-id-base64)
|
||||
(cdr message)
|
||||
@@ -1777,7 +1861,7 @@ Join with DISPLAY-NAME."
|
||||
(end (crdt--find-id (base64-decode-string end-id-base64) end-hint rear-advance)))
|
||||
(let ((crdt--inhibit-overlay-advices t))
|
||||
(move-overlay ov start end)))))))
|
||||
(crdt--broadcast-maybe (crdt--format-message message) (process-get process 'client-id)))
|
||||
(crdt--broadcast-maybe (crdt--format-message message) nil))
|
||||
|
||||
(defun crdt--delete-overlay-advice (orig-fun ov)
|
||||
(unless crdt--inhibit-overlay-advices
|
||||
@@ -1832,7 +1916,7 @@ Join with DISPLAY-NAME."
|
||||
(when (memq (crdt--overlay-metadata-species meta) crdt--enabled-overlay-species)
|
||||
(let ((crdt--inhibit-overlay-advices t))
|
||||
(overlay-put ov prop value))))))))
|
||||
(crdt--broadcast-maybe (crdt--format-message message) (process-get process 'client-id)))
|
||||
(crdt--broadcast-maybe (crdt--format-message message) nil))
|
||||
|
||||
(advice-add 'make-overlay :around #'crdt--make-overlay-advice)
|
||||
|
||||
@@ -1867,5 +1951,126 @@ Join with DISPLAY-NAME."
|
||||
(cl-loop for command in '(org-cycle org-shifttab)
|
||||
do (advice-add command :around #'crdt--org-overlay-advice))
|
||||
|
||||
;;; pseudo process
|
||||
(cl-defstruct (crdt--pseudo-process (:constructor crdt--make-pseudo-process))
|
||||
buffer
|
||||
mark)
|
||||
|
||||
(defun crdt--pseudo-process-send-string (pseudo-process string)
|
||||
(with-current-buffer (crdt--pseudo-process-buffer pseudo-process)
|
||||
(crdt--broadcast-maybe (crdt--format-message
|
||||
`(process ,crdt--buffer-network-name ,string)))))
|
||||
|
||||
(defun crdt--process-send-string-advice (orig-func process string)
|
||||
(if (crdt--pseudo-process-p process)
|
||||
(crdt--pseudo-process-send-string process string)
|
||||
(funcall orig-func process string)))
|
||||
|
||||
(defun crdt--process-send-region-advice (orig-func process start end)
|
||||
(if (crdt--pseudo-process-p process)
|
||||
(crdt--pseudo-process-send-string process (buffer-substring-no-properties start end))
|
||||
(funcall orig-func process start end)))
|
||||
|
||||
(defun crdt--get-buffer-process-advice (orig-func buffer)
|
||||
(and buffer
|
||||
(setq buffer (get-buffer buffer))
|
||||
(with-current-buffer buffer
|
||||
(if (and crdt--session (not (crdt--server-p)))
|
||||
crdt--buffer-pseudo-process
|
||||
(funcall orig-func buffer)))))
|
||||
|
||||
(defun crdt--get-process-advice (orig-func name)
|
||||
(if (crdt--pseudo-process-p name)
|
||||
name
|
||||
(funcall orig-func name)))
|
||||
|
||||
(defun crdt--process-mark-advice (orig-func process)
|
||||
(if (crdt--pseudo-process-p process)
|
||||
(crdt--pseudo-process-mark process)
|
||||
(funcall orig-func process)))
|
||||
|
||||
(defun crdt--process-name-advice (orig-func process)
|
||||
(if (crdt--pseudo-process-p process)
|
||||
process
|
||||
(funcall orig-func process)))
|
||||
|
||||
(cl-defmethod crdt-process-message ((message (head process-mark)) process)
|
||||
(cl-destructuring-bind (buffer-name crdt-id position-hint) (cdr message)
|
||||
(crdt--with-buffer-name
|
||||
buffer-name
|
||||
(save-excursion
|
||||
(goto-char (crdt--id-to-pos crdt-id position-hint))
|
||||
(let ((buffer-process (get-buffer-process (current-buffer))))
|
||||
(if buffer-process
|
||||
(progn (set-marker (process-mark buffer-process) (point))
|
||||
(setq crdt--last-process-mark-id crdt-id)
|
||||
(crdt--broadcast-maybe (crdt--format-message message) nil))
|
||||
(unless (crdt--server-p)
|
||||
(setq crdt--buffer-pseudo-process
|
||||
(crdt--make-pseudo-process :buffer (current-buffer) :mark (point-marker)))
|
||||
(setq crdt--last-process-mark-id crdt-id))))))))
|
||||
|
||||
(defun crdt--send-process-mark-maybe ()
|
||||
(let ((buffer-process (get-buffer-process (current-buffer))))
|
||||
(when buffer-process
|
||||
(let* ((mark-pos (marker-position (process-mark buffer-process)))
|
||||
(current-id (crdt--get-id mark-pos)))
|
||||
(unless (string-equal crdt--last-process-mark-id current-id)
|
||||
(setq crdt--last-process-mark-id current-id)
|
||||
(crdt--broadcast-maybe
|
||||
(crdt--format-message
|
||||
`(process-mark ,crdt--buffer-network-name
|
||||
,current-id ,mark-pos))))))))
|
||||
|
||||
(defun crdt--process-status-advice (orig-func process)
|
||||
(if (crdt--pseudo-process-p process)
|
||||
'run
|
||||
(funcall orig-func process)))
|
||||
|
||||
(defun crdt--delete-process-advice (orig-func process)
|
||||
(unless (crdt--pseudo-process-p process)
|
||||
(funcall orig-func process)))
|
||||
|
||||
(defun crdt--process-buffer-advice (orig-func process)
|
||||
(if (crdt--pseudo-process-p process)
|
||||
(crdt--pseudo-process-buffer process)
|
||||
(funcall orig-func process)))
|
||||
|
||||
(defun crdt--processp-advice (orig-func object)
|
||||
(or (crdt--pseudo-process-p object) (funcall orig-func object)))
|
||||
|
||||
(defun crdt--dummy () nil)
|
||||
|
||||
(defun crdt--process-sentinel/filter-advice (orig-func process)
|
||||
(if (crdt--pseudo-process-p process)
|
||||
#'crdt--dummy
|
||||
(funcall orig-func process)))
|
||||
|
||||
(defun crdt--set-process-sentinel/filter-advice (orig-func process func)
|
||||
(if (crdt--pseudo-process-p process)
|
||||
nil
|
||||
(funcall orig-func process func)))
|
||||
|
||||
(advice-add 'process-send-string :around #'crdt--process-send-string-advice)
|
||||
(advice-add 'process-send-region :around #'crdt--process-send-region-advice)
|
||||
(advice-add 'processp :around #'crdt--processp-advice)
|
||||
(advice-add 'get-buffer-process :around #'crdt--get-buffer-process-advice)
|
||||
(advice-add 'get-process :around #'crdt--get-process-advice)
|
||||
(advice-add 'process-status :around #'crdt--process-status-advice)
|
||||
(advice-add 'process-buffer :around #'crdt--process-buffer-advice)
|
||||
(advice-add 'process-mark :around #'crdt--process-mark-advice)
|
||||
(advice-add 'delete-process :around #'crdt--delete-process-advice)
|
||||
(advice-add 'process-name :around #'crdt--process-name-advice)
|
||||
(advice-add 'process-sentinel :around #'crdt--process-sentinel/filter-advice)
|
||||
(advice-add 'process-filter :around #'crdt--process-sentinel/filter-advice)
|
||||
(advice-add 'set-process-sentinel :around #'crdt--set-process-sentinel/filter-advice)
|
||||
(advice-add 'set-process-filter :around #'crdt--set-process-sentinel/filter-advice)
|
||||
|
||||
(cl-defmethod crdt-process-message ((message (head process)) process)
|
||||
(cl-destructuring-bind (buffer-name string) (cdr message)
|
||||
(crdt--with-buffer-name
|
||||
buffer-name
|
||||
(process-send-string (get-buffer-process (current-buffer)) string))))
|
||||
|
||||
(provide 'crdt)
|
||||
;;; crdt.el ends here
|
||||
|
||||
Reference in New Issue
Block a user