emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/crdt e2b6c9ebf6 22/44: get sharing buffer from clients


From: ELPA Syncer
Subject: [elpa] externals/crdt e2b6c9ebf6 22/44: get sharing buffer from clients working
Date: Sat, 2 Jul 2022 22:57:33 -0400 (EDT)

branch: externals/crdt
commit e2b6c9ebf61d16053c770a43fee842468ca3e1af
Author: Qiantan Hong <qhong@mit.edu>
Commit: Qiantan Hong <qhong@mit.edu>

    get sharing buffer from clients working
---
 HACKING.org |  23 +-
 crdt.el     | 713 +++++++++++++++++++++++++++++++++++-------------------------
 2 files changed, 422 insertions(+), 314 deletions(-)

diff --git a/HACKING.org b/HACKING.org
index 3bd19c85a3..b593ec0d01 100644
--- a/HACKING.org
+++ b/HACKING.org
@@ -30,7 +30,7 @@ Every message takes the form =(type . body)=
 
   - Text Editing
     A peer must obtain a =site-id= before performing the following operations,
-    by remote calling =crdt-get-write-access=. See [[Remote Command]].
+    by remote calling =crdt-get-write-access=. See [[Remote Function]].
     + insert ::
       body takes the form =(buffer-name user-id crdt-id position-hint content)=
       - =position-hint= is the buffer position where the operation happens at 
the site
@@ -143,26 +143,24 @@ Every message takes the form =(type . body)=
     + overlay-remove ::
       body takes the form =(buffer-name user-id logical-clock)=
 
-  - <<Remote Command>>
+  - <<Remote Function>>
     + fcap ::
-      body takes the form =(buffer-name command-symbol nonce in-states 
out-states)=
+      body takes the form =(fcap-symbol nonce in-states out-states . 
interactive-form)=
       This grants a "functional capability" to a peer.
       Nonce is a random number to prevent forging capability.
-      - =buffer-name= can also be =nil=, which means this is a session-scoped 
command,
-        not bound to any specific buffer.
-      - =in-states= is a list of state symbols that the command depends on.
-        =out-states= is a list of state symbols that the command modifies and 
should be synchronized
+      - =in-states= is a list of state symbols that the function depends on.
+        =out-states= is a list of state symbols that the function modifies and 
should be synchronized
         to the caller.
         See [[Allowed state symbols]].
 
-    + command ::
+    + funcall ::
       body takes the form
       #+BEGIN_SRC
-      (buffer-name user-id logical-clock
-       spawn-user-id state-list nonce command-symbol . args)
+      (user-id logical-clock spawn-user-id 
+        state-list nonce fcap-symbol . args)
       #+END_SRC
       - =spawn-user-id= represents the site where the interactive command is 
originally invoked
-        + It can be different from =user-id= because a remote command can call 
a remote command!
+        + It can be different from =user-id= because a remote function can 
call a remote function!
           This is especially useful when client makes a remote call, 
           but the call on the server request some interactive input,
           and such interactive call are remote-called back into the client.
@@ -171,7 +169,8 @@ Every message takes the form =(type . body)=
        (CDRs can also be 2 element list of the form =(crdt-id pos-hint)=)
        <<Allowed state symbols>> are 
        #+BEGIN_SRC
-       buffer point mark mark-active transient-mark-mode last-command-event
+       window window-point buffer buffer-content point
+       mark mark-active transient-mark-mode last-command-event
        #+END_SRC
 
     + return ::
diff --git a/crdt.el b/crdt.el
index 1b08215de4..c25dfed32e 100644
--- a/crdt.el
+++ b/crdt.el
@@ -83,12 +83,19 @@
   "Help string for `crdt-read-settings'."
   :type 'string)
 
-(defcustom crdt-default-session-command-functions
-  '((crdt-get-write-access)
-    crdt-xref-command-function)
-  "A list that describes default policies for public session-scoped commands.
+(defcustom crdt-default-session-permissions
+  '(crdt-write-access-fcaps
+    crdt-create-buffer-fcaps
+    crdt-comint-fcaps
+    crdt-xscheme-fcaps
+    crdt-xref-fcaps)
+  "A list that describes default policies for public session-scoped functions.
 See `crdt-new-session'.'"
-  :type '(list (or function (list function))))
+  :type '(list (or function symbol)))
+
+(defcustom crdt-override-command t
+  "Override local commands with corresponding remote commands when available."
+  :type 'boolean)
 
 ;;; Pseudo cursor/region utils
 
@@ -301,6 +308,16 @@ Must be used inside CRDT--WITH-INSERTION-INFORMATION."
                 (:constructor crdt--make-contact-metadata (display-name 
focused-buffer-name host service)))
   display-name host service focused-buffer-name)
 
+(cl-defstruct (crdt-remote-fcap
+                (:constructor crdt--make-remote-fcap
+                              (name nonce in-states out-states 
interactive-form)))
+  name nonce in-states out-states interactive-form)
+
+(cl-defstruct (crdt-local-fcap
+                (:constructor crdt--make-local-fcap
+                              (name nonce in-states out-states proxy)))
+  name nonce in-states out-states proxy)
+
 (cl-defstruct (crdt--session (:constructor crdt--make-session))
   local-id                              ; Local user-id
   local-clock                           ; Local logical clock
@@ -313,11 +330,11 @@ Must be used inside CRDT--WITH-INSERTION-INFORMATION."
   network-process
   network-clients
   next-user-id
-  (buffer-table (make-hash-table :test 'equal)); maps buffer network name to 
buffer
+  (buffer-table (make-hash-table :test 'equal)) ; maps buffer network name to 
buffer
   follow-user-id
-  command-functions
-  (fcap-in-table (make-hash-table :test 'eq))
-  (fcap-out-table (make-hash-table :test 'eq)))
+  permissions
+  (local-fcap-table (make-hash-table))
+  (remote-fcap-table (make-hash-table)))
 
 (defvar crdt--inhibit-update nil "When set, don't call CRDT--LOCAL-* on change.
 This is useful for functions that apply remote change to local buffer,
@@ -378,21 +395,6 @@ adding/removing actively tracked overlays.")
 (defvar-local crdt--enabled-text-properties nil
   "A list of text properties that are tracked and synchronized.")
 
-(defvar-local crdt-buffer-command-functions nil
-  "A list that describes policies for public buffer-local commands.
-Each element should be one of
-- a function, which should return a list of commands when
-  called with a single argument USER-ID.
-  The returned list of commands is made accessible to the user with USER-ID.
-- a list of commands.
-  These commands are made accessible to every user.")
-
-(defvar-local crdt-buffer-fcap-in-table nil
-  "A hash table that maps local command symbol to a nonce.")
-
-(defvar-local crdt-buffer-fcap-out-table nil
-  "A hash table that maps remote command symbol to a function.")
-
 ;;; Global variables
 
 (defvar crdt--session-list nil)
@@ -455,22 +457,25 @@ Also set CRDT--PSEUDO-CURSOR-TABLE to NIL."
           (setq crdt--pseudo-cursor-table (make-hash-table)))
         (unless crdt--overlay-table
           (setq crdt--overlay-table (make-hash-table :test 'equal)))
-        (unless crdt-buffer-fcap-in-table
-          (setq crdt-buffer-fcap-in-table (make-hash-table)))
-        (unless crdt-buffer-fcap-out-table
-          (setq crdt-buffer-fcap-out-table (make-hash-table)))
         (setq crdt--site-id-list (list 0 crdt--max-value))
         (crdt--install-hooks))
     (crdt--uninstall-hooks)
     (crdt--clear-pseudo-cursor-table)
-    (setq crdt--overlay-table nil
-          crdt-buffer-fcap-in-table nil
-          crdt-buffer-fcap-out-table nil
-          crdt--site-id-list nil)))
+    (setq crdt--overlay-table nil crdt--site-id-list nil
+          crdt--buffer-network-name nil)))
 
 (defun crdt--clone-buffer-hook ()
   (crdt-mode -1))
 
+(defvar crdt-mode-map
+  (let ((map (make-sparse-keymap)))
+    (define-key map (kbd "M-X") #'crdt-M-x)
+    map))
+
+(or (assq 'crdt-mode minor-mode-map-alist)
+    (nconc minor-mode-map-alist
+           (list (cons 'crdt-mode crdt-mode-map))))
+
 ;;; Author visualization
 
 (defsubst crdt--visualize-author-1 (beg end user-id)
@@ -580,12 +585,14 @@ If such buffer doesn't exist yet, do nothing."
   (declare (indent 1) (debug (sexp def-body)))
   `(crdt--call-with-buffer-name ,name (lambda () ,@body)))
 
-(defmacro crdt--with-buffer-name-pull (name &rest body)
+(cl-defmacro crdt--with-buffer-name-pull ((name &key sync) &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, request it from the server,
 and store the body in CRDT--BUFFER-SYNC-CALLBACK to evaluate it
-after synchronization is completed."
+after synchronization is completed.
+If SYNC is non-nil, block (but allow process filters to run)
+until synchronization is completed, otherwise run body asynchronously."
   (declare (indent 1) (debug (sexp def-body)))
   `(let (crdt-buffer)
      (setq crdt-buffer (gethash ,name (crdt--session-buffer-table 
crdt--session)))
@@ -604,14 +611,21 @@ after synchronization is completed."
              (let ((crdt--inhibit-update t))
                (insert "Synchronizing with server...")
                (read-only-mode))
-             (setq crdt--buffer-sync-callback
-                   (lambda ()
-                     ,@body))))))))
+             ,(if sync
+                  `(let (sync-complete)
+                     (setq crdt--buffer-sync-callback
+                           (lambda () (setq sync-complete t)))
+                     (while (not sync-complete)
+                       (accept-process-output))
+                     ,@body)
+                  `(setq crdt--buffer-sync-callback
+                         (lambda ()
+                           ,@body)))))))))
 
 ;;; Session menu
 
 (defsubst crdt--get-session-names (server)
-  "Get session names for CRDT sessions (as in CRDT--SESSION-LIST).
+  "Get session names for CRDT sessions (from `crdt--session-list').
 If SERVER is non-NIL, return the list of names for server sessions.
 Otherwise, return the list of names for client sessions."
   (let (session-names)
@@ -745,7 +759,7 @@ Directly return the buffer network name under point if in 
the buffer menu."
    (let ((session (crdt--read-session-maybe)))
      (list session (crdt--read-buffer-maybe session))))
   (let ((crdt--session session))
-    (crdt--with-buffer-name-pull network-name
+    (crdt--with-buffer-name-pull (network-name)
       (switch-to-buffer-other-window (current-buffer)))))
 
 (defvar crdt-buffer-menu-mode-map
@@ -847,7 +861,7 @@ Directly return the user name under point if in the user 
menu."
           (cl-block nil
             (let* ((metadata (or (gethash user-id (crdt--session-contact-table 
crdt--session)) (cl-return)))
                    (buffer-name (or 
(crdt--contact-metadata-focused-buffer-name metadata) (cl-return))))
-              (crdt--with-buffer-name-pull buffer-name
+              (crdt--with-buffer-name-pull (buffer-name)
                (switch-to-buffer-other-window (current-buffer))
                (ignore-errors (goto-char (overlay-start (car (gethash user-id 
crdt--pseudo-cursor-table)))))
                t)))
@@ -905,7 +919,8 @@ Only server can perform this action."
     (crdt-user-menu-mode)
     (setq tabulated-list-entries nil)
     (push (list (crdt--session-local-id crdt--session)
-                (vector (crdt--session-local-name crdt--session) ""
+                (vector (prin1-to-string (crdt--session-local-id 
crdt--session))
+                        (crdt--session-local-name crdt--session) ""
                         (or (crdt--session-focused-buffer-name crdt--session) 
"--")
                         "*myself*"))
           tabulated-list-entries)
@@ -931,7 +946,9 @@ Only server can perform this action."
     (tabulated-list-print)))
 
 (defsubst crdt--refresh-users-maybe ()
-  "Refresh the user menu buffer for current session, if there's any."
+  "Refresh the user menu buffer for current session, if there's any.
+This function also calls `crdt--refresh-buffers-maybe', as changes in
+user menu almost always indicate supposed changes in buffer menu."
   (when (and (crdt--session-user-menu-buffer crdt--session) (buffer-live-p 
(crdt--session-user-menu-buffer crdt--session)))
     (crdt-refresh-users (crdt--session-user-menu-buffer crdt--session)))
   (crdt--refresh-buffers-maybe))
@@ -973,6 +990,102 @@ It informs other peers that the buffer is killed."
       (crdt-stop-share-buffer))
     (crdt--refresh-users-maybe)))
 
+;;; Capabilities
+
+(define-error 'crdt-invalid-fcap "Invalid CRDT fcap")
+(define-error 'crdt-no-permission "No CRDT permission")
+
+(defsubst crdt-get-fcap (fcap-symbol)
+  "Find the active `crdt-remote-fcap' with name FCAP-SYMBOL.
+Signal a `crdt-no-permission' error if no such fcap exists."
+  (or (gethash fcap-symbol (crdt--session-remote-fcap-table crdt--session))
+      (signal 'crdt-no-permission (list fcap-symbol))))
+
+(cl-defun crdt-make-local-fcap
+    (name &optional in-states out-states (proxy name))
+  "Return a new `crdt-local-fcap'.
+The fcap is initialized with NAME, IN-STATES, OUT-STATES and PROXY.
+When PROXY is nil, use NAME by default."
+  (crdt--make-local-fcap name nil in-states out-states proxy))
+
+(defun crdt-make-local-fcaps (fcap-entries)
+  "Return a list of new `crdt-local-fcap'.
+FCAP-ENTRIES should be a list with each element a valid lists
+of arguments to `crdt-make-local-fcap'.
+See `crdt-xscheme-fcaps' for an example of usage."
+  (mapcar (lambda (entry) (apply #'crdt-make-local-fcap entry))
+          fcap-entries))
+
+(defun crdt--generate-nonce ()
+  (with-temp-buffer
+    (toggle-enable-multibyte-characters 0)
+    (let ((err (call-process-shell-command "head -c 16 /dev/urandom" nil t)))
+      (unless (= err 0)
+        (error "Failed to read /dev/urandom (code %s)" err)))
+    (buffer-string)))
+
+(defun crdt--compute-user-fcaps (permission user-id)
+  (cl-loop for f in permission
+        if (functionp f)
+        append (funcall f user-id)
+        else if (symbolp f)
+        append (symbol-value f)
+        else
+        append f))
+
+(defun crdt--intern-fcap (fcap)
+  "Copy FCAP to current session's local fcap table.
+Generate a nonce if none has been generated before."
+  (let ((memo (gethash (crdt-local-fcap-name fcap)
+                       (crdt--session-local-fcap-table crdt--session)))
+        (fcap (cl-copy-seq fcap)))
+    (setf (crdt-local-fcap-nonce fcap)
+          (if memo (crdt-local-fcap-nonce memo) (crdt--generate-nonce)))
+    (puthash (crdt-local-fcap-name fcap) fcap
+             (crdt--session-local-fcap-table crdt--session))
+    fcap))
+
+(defun crdt-make-interactive-proxy (command-symbol)
+  "Return a function that calls COMMAND-SYMBOL interactively on remote 
machine."
+  (lambda () (interactive)
+    (let ((this-command command-symbol))
+      (call-interactively command-symbol))))
+
+(defun crdt-get-write-access ()
+  "Allocate a Site ID for current user to access current buffer.
+Current user means the user corresponding to CRDT--PROCESS."
+  (let (new-site-id)
+    (cl-loop
+          for i in crdt--site-id-list
+          for c on crdt--site-id-list
+          for j in (cdr crdt--site-id-list)
+          if (> j (1+ i))
+          do (progn
+               (setq new-site-id (1+ i))
+               (push (1+ i) (cdr c))
+               (cl-return)))
+    (unless new-site-id
+      (let* ((cons (last crdt--site-id-use-list 2))
+             (victim-id (cadr cons)))
+        ;; todo: notify the victim
+        (rplacd cons nil)
+        (setq new-site-id victim-id)))
+    (push new-site-id crdt--site-id-use-list)
+    (puthash (crdt--client-id) new-site-id crdt--site-id-table)
+    new-site-id))
+
+(defvar crdt-write-access-fcaps
+  (crdt-make-local-fcaps '((crdt-get-write-access (buffer)))))
+
+(defun crdt-get-buffer-create (network-name)
+  "Return the buffer with NETWORK-NAME, creating a new one if needed."
+  (or (gethash network-name (crdt--session-buffer-table crdt--session))
+      (crdt--share-buffer (generate-new-buffer network-name)
+                          crdt--session network-name)))
+
+(defvar crdt-create-buffer-fcaps
+  (crdt-make-local-fcaps '((crdt-get-buffer-create))))
+
 ;;; CRDT insert/delete
 
 (defsubst crdt--text-property-assimilate
@@ -1006,7 +1119,7 @@ Returns a list of (insert type) messages to be sent."
     (when crdt-visualize-author-mode
       (put-text-property beg end 'crdt-author user-id)
       (crdt--visualize-author-1 beg end user-id))
-    (let (resulting-commands)
+    (let (resulting-fcaps)
       (crdt--with-insertion-information (beg end)
         (unless (crdt--split-maybe)
          (when (and not-begin
@@ -1022,7 +1135,7 @@ Returns a list of (insert type) messages to be sent."
                  (push `(insert ,crdt--buffer-network-name ,user-id
                                  ,virtual-id ,beg
                                  ,(crdt--buffer-substring beg merge-end))
-                        resulting-commands))
+                        resulting-fcaps))
                 (cl-incf left-offset (- merge-end beg))
                 (setq beg merge-end)))))
         (while (< beg end)
@@ -1035,12 +1148,12 @@ Returns a list of (insert type) messages to be sent."
               (push `(insert ,crdt--buffer-network-name ,user-id
                              ,new-id ,beg
                              ,(crdt--buffer-substring beg block-end))
-                    resulting-commands)
+                    resulting-fcaps)
               (setq beg block-end)
               (setq left-offset (1- crdt--max-value)) ; this is always true 
when we need to continue
               (setq starting-id new-id)))))
       ;; (crdt--verify-buffer)
-      (nreverse resulting-commands))))
+      (nreverse resulting-fcaps))))
 
 (defun crdt--find-id (id pos &optional before)
   "Find the first position *after* ID if BEFORE is NIL or *before* ID 
otherwise.
@@ -1188,8 +1301,9 @@ Request a Site ID if we don't have it yet."
     (setq crdt--changed-start beg)
     (unless crdt--site-id
       (condition-case nil
-          (setq crdt--site-id (crdt-remote-call 'crdt-get-write-access))
-        (crdt-access-denied
+          (setq crdt--site-id
+                (crdt-remote-apply (crdt-get-fcap 'crdt-get-write-access) nil))
+        (crdt-invalid-fcap
          (read-only-mode)
          (warn "Write access revoked in %s" crdt--buffer-network-name)
          (signal 'quit nil))))))
@@ -1547,17 +1661,7 @@ CRDT--PROCESS should be bound to the network process for 
the client connection."
                crdt--overlay-table)
 
       (crdt--send-process-mark-maybe nil)
-      (crdt--send-variables-maybe nil)
-
-      ;; send fcaps
-      (dolist (command (crdt--compute-user-commands 
crdt-buffer-command-functions (crdt--client-id)))
-        (let ((nonce (or (gethash command crdt-buffer-fcap-in-table)
-                         (puthash command (crdt--generate-nonce) 
crdt-buffer-fcap-in-table))))
-          (process-send-string crdt--process
-                               (crdt--format-message
-                                `(fcap ,crdt--buffer-network-name ,command 
,nonce
-                                       ,(get command 'crdt-out-states)
-                                       ,(get command 'crdt-in-states)))))))))
+      (crdt--send-variables-maybe nil))))
 
 (defun crdt--greet-client ()
   "Send initial information when a client connects.
@@ -1575,7 +1679,7 @@ CRDT--PROCESS should be bound to The network process for 
the client connection."
                                               ,(crdt--session-name 
crdt--session))))
         (cl-incf (crdt--session-next-user-id crdt--session)))
       (process-send-string crdt--process (crdt--format-message
-                                    (cons 'add (hash-table-keys 
(crdt--session-buffer-table crdt--session)))))
+                                          `(add ,@(hash-table-keys 
(crdt--session-buffer-table crdt--session)))))
       ;; synchronize contact
       (maphash (lambda (k v)
                  (process-send-string crdt--process
@@ -1600,15 +1704,17 @@ CRDT--PROCESS should be bound to The network process 
for the client connection."
                                        ,(process-contact crdt--process 
:service))))
         (crdt-process-message-1 contact-message))
       ;; send fcaps
-      (dolist (command (crdt--compute-user-commands 
(crdt--session-command-functions crdt--session) client-id))
-        (let ((nonce (or (gethash command (crdt--session-fcap-in-table 
crdt--session))
-                         (puthash command (crdt--generate-nonce)
-                                  (crdt--session-fcap-in-table 
crdt--session)))))
-          (process-send-string crdt--process
-                               (crdt--format-message
-                                `(fcap nil ,command ,nonce
-                                       ,(get command 'crdt-out-states)
-                                       ,(get command 'crdt-in-states)))))))))
+      (dolist (fcap (crdt--compute-user-fcaps
+                        (crdt--session-permissions crdt--session)
+                        client-id))
+        (setq fcap (crdt--intern-fcap fcap))
+        (process-send-string crdt--process
+                             (crdt--format-message
+                              `(fcap ,(crdt-local-fcap-name fcap)
+                                     ,(crdt-local-fcap-nonce fcap)
+                                     ,(crdt-local-fcap-in-states fcap)
+                                     ,(crdt-local-fcap-out-states fcap)
+                                     ,@ (interactive-form 
(crdt-local-fcap-proxy fcap)))))))))
 
 (define-crdt-message-handler insert (buffer-name user-id crdt-id position-hint 
content)
   (crdt--with-buffer-name buffer-name
@@ -1656,17 +1762,19 @@ CRDT--PROCESS should be bound to The network process 
for the client connection."
     (crdt--refresh-buffers-maybe)))
 
 (define-crdt-message-handler ready (buffer-name mode)
-  (unless (crdt--server-p)           ; server shouldn't receive this
-    (crdt--with-buffer-name buffer-name
-      (unless (fboundp mode)
-        (when (get mode 'crdt-autoload)
-          (require (get mode 'crdt-autoload) nil t)))
-      (if (fboundp mode)
-          (unless (eq major-mode mode)
-            (funcall mode)              ; trust your server...
-            (crdt-mode))
-        (warn "Server uses %s, but not available locally." mode))
-      (when (crdt-get-fcap 'crdt-get-write-access)
+  (crdt--with-buffer-name buffer-name
+    (unless (fboundp mode)
+      (when (get mode 'crdt-autoload)
+        (require (get mode 'crdt-autoload) nil t)))
+    (if (fboundp mode)
+        (unless (eq major-mode mode)
+          (if (not (string-match-p "-mode$" (symbol-name mode))) ; An ad-hoc 
security check...
+              (warn "Remote session uses mode %s, but I refuse to turn it on 
because it doesn't look like a mode." mode)
+            (funcall mode)
+            (crdt-mode)))
+      (warn "Remote session uses mode %s, but not available locally." mode))
+    (unless (crdt--server-p)
+      (when (gethash 'crdt-get-write-access (crdt--session-remote-fcap-table 
crdt--session))
         (read-only-mode -1))
       (when crdt--buffer-sync-callback
         (funcall crdt--buffer-sync-callback)
@@ -1695,26 +1803,29 @@ CRDT--PROCESS should be bound to The network process 
for the client connection."
     (crdt--refresh-buffers-maybe)))
 
 (define-crdt-message-handler remove (&rest buffer-names)
-  (let ((saved-session crdt--session))
-    (dolist (buffer-name buffer-names)
-      (let ((buffer (gethash buffer-name (crdt--session-buffer-table 
crdt--session))))
-        (remhash buffer-name (crdt--session-buffer-table crdt--session))
-        (when buffer
-          (when (buffer-live-p buffer)
-            (with-current-buffer buffer
-              (crdt-mode 0)
-              (setq crdt--session nil))))))
-    (let ((notify-names
-           (cl-remove-if-not
-            (lambda (buffer-name)
-              (gethash buffer-name (crdt--session-buffer-table crdt--session)))
-            buffer-names)))
-      (when notify-names
-        (warn "Server stopped sharing %s."
-              (mapconcat #'identity buffer-names ", "))))
-   (let ((crdt--session saved-session))
-     (crdt--broadcast-maybe crdt--message-string (when crdt--process 
(crdt--client-id)))
-     (crdt--refresh-buffers-maybe))))
+  (let ((notify-names
+         (cl-remove-if-not
+          (lambda (buffer-name)
+            (gethash buffer-name (crdt--session-buffer-table crdt--session)))
+          buffer-names)))
+    (when notify-names
+      (warn "Server stopped sharing %s."
+            (mapconcat #'identity buffer-names ", "))))
+  (dolist (buffer-name buffer-names)
+    (let ((buffer (gethash buffer-name (crdt--session-buffer-table 
crdt--session))))
+      (remhash buffer-name (crdt--session-buffer-table crdt--session))
+      (when buffer
+        (when (buffer-live-p buffer)
+          (with-current-buffer buffer
+            (crdt-mode 0)
+            (setq-local crdt--session nil))))))
+  ;; update focused buffer
+  (maphash (lambda (_k v)
+             (when (member (crdt--contact-metadata-focused-buffer-name v) 
buffer-names)
+               (setf (crdt--contact-metadata-focused-buffer-name v) nil)))
+           (crdt--session-contact-table crdt--session))
+  (crdt--broadcast-maybe crdt--message-string (when crdt--process 
(crdt--client-id)))
+  (crdt--refresh-users-maybe))
 
 (define-crdt-message-handler login (id session-name)
   (puthash 0 (crdt--make-contact-metadata nil nil
@@ -1761,7 +1872,7 @@ CRDT--PROCESS should be bound to The network process for 
the client connection."
   ;;   (setq crdt--focused-buffer-name buffer-name)
   ;;   (switch-to-buffer (gethash buffer-name (crdt--session-buffer-table 
crdt--session))))
   (when (eq user-id (crdt--session-follow-user-id crdt--session))
-    (crdt--with-buffer-name-pull buffer-name
+    (crdt--with-buffer-name-pull (buffer-name)
       (switch-to-buffer (current-buffer))
       (let ((ov-pair (gethash user-id crdt--pseudo-cursor-table)))
         (when ov-pair (goto-char (overlay-start (car ov-pair)))))))
@@ -1848,48 +1959,6 @@ Handle received STRING from PROCESS."
     (ding)
     (crdt--stop-session (process-get process 'crdt-session))))
 
-;;; Capabilities
-
-(define-error 'crdt-access-denied "CRDT access denied")
-
-(defun crdt--generate-nonce ()
-  (with-temp-buffer
-    (toggle-enable-multibyte-characters 0)
-    (let ((err (call-process-shell-command "head -c 8 /dev/urandom" nil t)))
-      (unless (= err 0)
-        (error "Failed to read /dev/urandom (code %s)" err)))
-    (buffer-string)))
-
-(defun crdt--compute-user-commands (command-function user-id)
-  (cl-loop for f in command-function
-        if (functionp f)
-        append (funcall f user-id)
-        else
-        append f))
-
-(defun crdt-get-write-access ()
-  "Allocate a Site ID for current user to access current buffer.
-Current user means the user corresponding to CRDT--PROCESS."
-  (let (new-site-id)
-    (cl-loop
-          for i in crdt--site-id-list
-          for c on crdt--site-id-list
-          for j in (cdr crdt--site-id-list)
-          if (> j (1+ i))
-          do (progn
-               (setq new-site-id (1+ i))
-               (push (1+ i) (cdr c))
-               (cl-return)))
-    (unless new-site-id
-      (let* ((cons (last crdt--site-id-use-list 2))
-             (victim-id (cadr cons)))
-        ;; todo: notify the victim
-        (rplacd cons nil)
-        (setq new-site-id victim-id)))
-    (push new-site-id crdt--site-id-use-list)
-    (puthash (crdt--client-id) new-site-id crdt--site-id-table)
-    new-site-id))
-
 ;;; UI commands
 
 (defvar crdt--ephemeral-advices nil)
@@ -1989,24 +2058,56 @@ SETTINGS-LIST."
         (unless (< (length (window-list)) 2)
           (delete-window (get-buffer-window (current-buffer))))))))
 
-(defun crdt--share-buffer (buffer session)
-  "Add BUFFER to CRDT SESSION."
-  (if (process-contact (crdt--session-network-process session) :server)
-      (with-current-buffer buffer
-        (setq crdt--session session)
-        (puthash (buffer-name buffer) buffer (crdt--session-buffer-table 
crdt--session))
-        (setq crdt--buffer-network-name (buffer-name buffer)
-              crdt--site-id 0 crdt--site-id-table (make-hash-table))
-        (crdt-mode)
-        (save-excursion
-          (save-restriction
-            (widen)
-            (let ((crdt--inhibit-update t))
-              (with-silent-modifications
-                (crdt--local-insert (point-min) (point-max))))))
-        (crdt--refresh-buffers-maybe)
-        (crdt--refresh-sessions-maybe))
-    (error "Only server can add new buffer")))
+(defun crdt--generate-new-name (name names format)
+  "Return a string not in NAMES based on NAME and FORMAT."
+  (if (member name names)
+      (cl-loop for i from 1
+            for new-name = (format format name (number-to-string i))
+            unless (member new-name names)
+            do (cl-return new-name))
+    name))
+
+(cl-defun crdt--share-buffer
+    (buffer &optional (session crdt--session) (network-name (buffer-name 
buffer)))
+  "Add BUFFER to CRDT SESSION with NETWORK-NAME.
+Possibly add a suffix to NETWORK-NAME to avoid duplicated network names.
+Return BUFFER."
+  (cl-flet ((content-message ()
+              (save-excursion
+                (save-restriction
+                  (widen)
+                  (let ((crdt--inhibit-update t))
+                    (with-silent-modifications
+                      (crdt--local-insert (point-min) (point-max))))))))
+    (let ((new-name (crdt--generate-new-name
+                     network-name
+                     (hash-table-keys (crdt--session-buffer-table session))
+                     "%s<%s>")))
+      (if (process-contact (crdt--session-network-process session) :server)
+          (with-current-buffer buffer
+            (setq crdt--session session)
+            (puthash new-name buffer (crdt--session-buffer-table 
crdt--session))
+            (setq crdt--buffer-network-name new-name
+                  crdt--site-id 0 crdt--site-id-table (make-hash-table))
+            (crdt-mode)
+            (content-message)
+            (crdt--broadcast-maybe
+             (crdt--format-message `(add ,crdt--buffer-network-name))
+             crdt--remote-call-spawn-user-id)
+            (crdt--refresh-buffers-maybe)
+            (crdt--refresh-sessions-maybe)
+            buffer)
+        (with-current-buffer buffer
+          (setq crdt--buffer-network-name new-name
+                crdt--session session)
+          (crdt-remote-apply (crdt-get-fcap 'crdt-get-buffer-create) (list 
new-name))
+
+          (setq crdt--site-id (crdt-remote-apply (crdt-get-fcap 
'crdt-get-write-access) nil))
+          (puthash new-name buffer (crdt--session-buffer-table crdt--session))
+          (crdt-mode)
+          (dolist (message (content-message))
+            (crdt--broadcast-maybe (crdt--format-message message)))
+          (crdt--broadcast-maybe (crdt--format-message `(ready 
,crdt--buffer-network-name ,major-mode))))))))
 
 (defun crdt--settings-make-ensure-type (type-predicate)
   (lambda (string)
@@ -2028,15 +2129,10 @@ Create a new one if such a CRDT session doesn't exist."
    (progn
      (when (and crdt-mode crdt--session)
        (error "Current buffer is already shared in a CRDT session"))
-     (list (let* ((session-names (crdt--get-session-names t))
-                  (default-name (if (member crdt-default-session-name 
session-names)
-                                    (cl-loop for i from 1
-                                          for name = (concat 
crdt-default-session-name "_" (number-to-string i))
-                                          unless (member name session-names)
-                                          do (cl-return name))
-                                    crdt-default-session-name))
+     (list (let* ((session-names (mapcar #'crdt--session-name 
crdt--session-list))
+                  (default-name (crdt--generate-new-name 
crdt-default-session-name session-names "%s_%s"))
                   (session-name (if session-names
-                                    (completing-read "Choose a server session 
(create if not exist): "
+                                    (completing-read "Choose a session (create 
if not exist): "
                                                      session-names)
                                   default-name)))
              session-name))))
@@ -2052,7 +2148,7 @@ Create a new one if such a CRDT session doesn't exist."
                    ("Password: " "")
                    ("Display Name: " ,crdt-default-name)
                    ("Command Functions: "
-                    ,(prin1-to-string crdt-default-session-command-functions)
+                    ,(prin1-to-string crdt-default-session-permissions)
                     ,(crdt--settings-make-ensure-type 'listp)))))))))
 
 (cl-defun crdt-stop-share-buffer (&optional (session crdt--session)
@@ -2069,18 +2165,20 @@ Create a new one if such a CRDT session doesn't exist."
     (message "Not a CRDT shared buffer.")))
 
 (defun crdt-new-session
-    (port session-name password display-name command-functions)
+    (port session-name password display-name permissions)
   "Start a new CRDT session on PORT with SESSION-NAME.
 Setup up the server with PASSWORD and assign this Emacs DISPLAY-NAME.
-COMMAND-FUNCTIONS is a list that describes policies
-for public session-scoped commands.
+PERMISSIONS is a list that describes policies
+for public session-scoped functionss.
 Each element should be one of
-- a function, which should return a list of commands when
-  called with a single argument USER-ID..
-  The returned list of commands is made accessible
+- a function, which should return a list of `crdt-local-fcap's when
+  called with a single argument USER-ID.
+  The returned list of fcaps is made accessible
   to the user with USER-ID in every buffer.
-- a list of commands.
-  These commands are made accessible to every user, in every buffer."
+- a symbol, which has a list of `crdt-local-fcap's as its value.
+  These fcaps are made accessible to every user, in every buffer.
+- a list of `crdt-local-fcap's
+  These fcaps are made accessible to every user, in every buffer."
   (let* ((network-process (make-network-process
                            :name "CRDT Server"
                            :server t
@@ -2095,7 +2193,7 @@ Each element should be one of
                               :local-name display-name
                               :name session-name
                               :network-process network-process
-                              :command-functions command-functions))
+                              :permissions permissions))
          (tuntox-p (or (eq crdt-use-tuntox t)
                        (and (eq crdt-use-tuntox 'confirm)
                             (yes-or-no-p "Start a tuntox proxy for this 
session? ")))))
@@ -2449,9 +2547,9 @@ Join with DISPLAY-NAME."
   "Register for autoloading FEATURE before CRDT enforce major MODE."
   (put mode 'crdt-autoload feature))
 
-;;; Remote Command
+;;; Remote Function
 
-(defun crdt--assemble-state-list (states)
+(cl-defun crdt--assemble-state-list (states &optional (session crdt--session))
   (let (result)
     (cl-labels ((collect (entry tail)
                   (unless (assq entry result) (push (cons entry tail) result)))
@@ -2459,9 +2557,15 @@ Join with DISPLAY-NAME."
                   (cl-ecase entry
                     ((region) (mapc #'process '(point mark mark-active 
transient-mark-mode)))
                     ((buffer)
-                     (unless crdt--buffer-network-name ;; TODO: capability safe
-                       (crdt--share-buffer (current-buffer) crdt--session))
+                     (unless (and crdt--session crdt--buffer-network-name) ;; 
TODO: capability safe
+                       (crdt--share-buffer (current-buffer) session))
                      (collect entry (list crdt--buffer-network-name)))
+                    ((window)
+                     (with-current-buffer (window-buffer (selected-window))
+                       (unless (and crdt--session crdt--buffer-network-name) 
;; TODO: capability safe
+                         (crdt--share-buffer (current-buffer) session))
+                       (collect entry (list crdt--buffer-network-name
+                                            (window-point 
(selected-window))))))
                     ((point) (process 'buffer)
                      (collect entry (list (crdt--get-id (point)) (point))))
                     ((mark) (process 'buffer)
@@ -2471,16 +2575,20 @@ Join with DISPLAY-NAME."
       (mapc #'process states))
     (nreverse result)))
 
-(defun crdt--apply-state-list (state-list &optional switch-to-buffer)
+(defun crdt--apply-state-list (state-list)
   (let (vars vals)
     (cl-loop for entry in state-list
           for rest on state-list
           do (cl-case (car entry)
                ((buffer)
-                (crdt--with-buffer-name-pull (cadr entry)
-                  (crdt--apply-state-list (cdr rest))
-                  (when switch-to-buffer (switch-to-buffer (current-buffer))))
-                (cl-return))
+                (set-buffer
+                 (crdt--with-buffer-name-pull ((cadr entry) :sync t)
+                   (current-buffer))))
+               ((window)
+                (switch-to-buffer
+                 (crdt--with-buffer-name-pull ((cadr entry) :sync t)
+                   (current-buffer)))
+                (set-window-point (selected-window) (caddr entry)))
                ((point) (goto-char (apply #'crdt--id-to-pos (cdr entry))))
                ((mark) (set-mark (apply #'crdt--id-to-pos (cdr entry))))
                ((mark-active transient-mark-mode last-command-event)
@@ -2492,53 +2600,43 @@ Join with DISPLAY-NAME."
   "The User ID where current remote call (if any) is orignally called.")
 
 (define-crdt-message-handler fcap
-    (buffer-name command-symbol nonce in-states _out-states)
-  (cl-flet* ((body ()
-                   (puthash command-symbol
-                            (lambda (&rest args)
-                              (crdt--remote-call (crdt--session-local-id 
crdt--session)
-                                                 command-symbol nonce 
in-states args))
-                            (if buffer-name crdt-buffer-fcap-out-table
-                              (crdt--session-fcap-out-table crdt--session)))
-                   (cl-case command-symbol
-                     ((crdt-get-write-access)
-                      (if buffer-name
-                          (read-only-mode -1)
-                        (dolist (buffer (hash-table-values 
(crdt--session-buffer-table crdt--session)))
-                          (when buffer
-                            (with-current-buffer buffer (read-only-mode 
-1)))))))))
-    (if buffer-name (crdt--call-with-buffer-name buffer-name #'body) (funcall 
#'body))))
-
-(define-crdt-message-handler command
-    (buffer-name user-id logical-clock spawn-user-id
-                 state-list nonce command-symbol &rest args)
-  (cl-flet*
-      ((check ()
-              (or (equal nonce
-                         (gethash command-symbol (crdt--session-fcap-in-table 
crdt--session)))
-                  (and buffer-name
-                       (equal nonce
-                              (gethash command-symbol 
crdt-buffer-fcap-in-table)))))
-       (body ()
-             (let ((bindings (crdt--apply-state-list state-list)))
-               (cl-progv (car bindings) (cdr bindings)
-                 (let* ((crdt--inhibit-update nil)
-                        (crdt--remote-call-spawn-user-id spawn-user-id)
-                        (return-message
-                         (if (check)
-                             (save-mark-and-excursion
-                               (save-window-excursion
-                                 (condition-case err
-                                     (list t (apply command-symbol (mapcar 
#'crdt--readable-decode args)))
-                                   (error (list nil (car err) 
(crdt--readable-encode (cdr err)))))))
-                           (list nil 'crdt-access-denied))))
-                   (let ((msg (crdt--format-message
-                               `(return ,user-id ,logical-clock
-                                        ,(crdt--assemble-state-list (get 
command-symbol 'crdt-command-out-states))
-                                        ,@return-message))))
-                     (crdt--log-send-network-traffic msg)
-                     (process-send-string crdt--process msg)))))))
-    (if buffer-name (crdt--call-with-buffer-name buffer-name #'body) (funcall 
#'body))))
+    (fcap-symbol nonce in-states out-states &rest interactive-form)
+  (puthash fcap-symbol
+           (crdt--make-remote-fcap fcap-symbol nonce in-states out-states 
interactive-form)
+           (crdt--session-remote-fcap-table crdt--session))
+  (when crdt-override-command
+    (advice-add fcap-symbol :around 'crdt--remote-fcap-advice))
+  (cl-case fcap-symbol
+    ((crdt-get-write-access)
+     (dolist (buffer (hash-table-values (crdt--session-buffer-table 
crdt--session)))
+       (when buffer
+         (with-current-buffer buffer (read-only-mode -1)))))))
+
+(define-crdt-message-handler funcall
+    (user-id logical-clock spawn-user-id
+             state-list nonce fcap-symbol &rest args)
+  (let* ((crdt--inhibit-update nil)
+         (crdt--remote-call-spawn-user-id spawn-user-id)
+         (saved-session crdt--session)
+         (return-message
+          (condition-case err
+              (let ((fcap (gethash fcap-symbol (crdt--session-local-fcap-table 
crdt--session))))
+                (if fcap
+                    (if (string-equal nonce (crdt-local-fcap-nonce fcap))
+                        (save-mark-and-excursion
+                          (save-window-excursion
+                            (let ((bindings (crdt--apply-state-list 
state-list)))
+                              (cl-progv (car bindings) (cdr bindings)
+                                (prog1 (list t (apply (crdt-local-fcap-proxy 
fcap) (mapcar #'crdt--readable-decode args)))
+                                  (setq state-list (crdt--assemble-state-list 
(crdt-local-fcap-out-states fcap) saved-session)))))))
+                      (list nil 'crdt-invalid-fcap fcap-symbol))
+                  (list nil 'crdt-invalid-fcap fcap-symbol)))
+            (error (list nil (car err) (crdt--readable-encode (cdr err))))))
+         (msg (crdt--format-message
+               `(return ,user-id ,logical-clock
+                        ,state-list ,@(crdt--readable-encode 
return-message)))))
+    (crdt--log-send-network-traffic msg)
+    (process-send-string crdt--process msg)))
 
 (defvar crdt--return-message-table (make-hash-table))
 
@@ -2547,18 +2645,20 @@ Join with DISPLAY-NAME."
     (puthash logical-clock (cl-list* state-list success-p 
(crdt--readable-decode return-values))
              crdt--return-message-table)))
 
-(defun crdt--remote-call (spawn-user-id function-symbol nonce in-states args)
-  "Send remote call request (a command type message) for FUNCTION-SYMBOL.
-SPAWN-USER-ID is the site where the series (if any) of remote calls originally 
started.
-NONCE should be acquired from some fcap message for fcap verification.
-Assemble state list for items in IN-STATES. Request for calling 
FUNCTION-SYMBOL with ARGS."
+(cl-defun crdt-remote-apply
+    (remote-fcap args &optional (spawn-user-id (crdt--session-local-id 
crdt--session)))
+  "Remote call REMOTE-FCAP with ARGS.
+SPAWN-USER-ID is the user ID where the series of remote calls
+originally started."
   (let* ((user-id (crdt--session-local-id crdt--session))
          (logical-clock (crdt--session-local-clock crdt--session))
          (msg (crdt--format-message
-               `(command ,crdt--buffer-network-name ,user-id
-                         ,logical-clock  ,spawn-user-id
-                         ,(crdt--assemble-state-list in-states) ,nonce
-                         ,function-symbol ,@(mapcar #'crdt--readable-encode 
args)))))
+               `(funcall ,user-id
+                         ,logical-clock ,spawn-user-id
+                         ,(crdt--assemble-state-list 
(crdt-remote-fcap-in-states remote-fcap))
+                         ,(crdt-remote-fcap-nonce remote-fcap)
+                         ,(crdt-remote-fcap-name remote-fcap)
+                         ,@(mapcar #'crdt--readable-encode args)))))
     (crdt--log-send-network-traffic msg)
     (process-send-string (crdt--session-network-process crdt--session) msg)
     (cl-incf (crdt--session-local-clock crdt--session))
@@ -2567,34 +2667,45 @@ Assemble state list for items in IN-STATES. Request for 
calling FUNCTION-SYMBOL
     (let ((return-message (gethash logical-clock crdt--return-message-table)))
       (remhash logical-clock crdt--return-message-table)
       (cl-destructuring-bind (state-list success-p &rest return-values) 
return-message
-        (crdt--apply-state-list state-list t)
+        (crdt--apply-state-list state-list)
         (if success-p
             (car return-values)
-          (apply #'signal return-values))))))
-
-(defsubst crdt-get-fcap (command-symbol)
-  "Find buffer or session fcap with name COMMAND-SYMBOL."
-  (or (gethash command-symbol crdt-buffer-fcap-out-table)
-      (gethash command-symbol (crdt--session-fcap-out-table crdt--session))))
-
-(defun crdt-remote-call (command-symbol &rest args)
-  "Remote call COMMAND-SYMBOL with ARGS.
-Find and use buffer or session fcap with name COMMAND-SYMBOL."
-  (let ((fcap (crdt-get-fcap command-symbol)))
-    (if fcap (apply fcap args)
-      (signal 'crdt-access-denied command-symbol))))
-
-(defun crdt-register-remote-commands (command-entries)
-  "Register a list of remote commands according to COMMAND-ENTRIES.
-Each item in COMMAND-ENTRIES should have the form (COMMAND-SYMBOL &optional 
IN-STATES OUT-STATES)."
-  (dolist (entry command-entries)
-    (cl-destructuring-bind (command-symbol &optional in-states out-states) 
entry
-      (put command-symbol 'crdt-in-states in-states)
-      (put command-symbol 'crdt-out-states out-states))))
-
-(defun crdt-make-publish-command-hook (command-entries)
-  "Return a function that publish commands in COMMAND-ENTRIES for current 
buffer."
-  (push (mapcar #'car command-entries) crdt-buffer-command-functions))
+          (signal (car return-values) (cdr return-values)))))))
+
+(defun crdt-remote-call-interactively (remote-fcap spawn-user-id)
+  "Remote call REMOTE-FCAP interactively.
+SPAWN-USER-ID is the site where the series of remote calls originally started."
+  (crdt-remote-apply remote-fcap
+                    (call-interactively
+                     `(lambda (&rest args)
+                        ,(crdt-remote-fcap-interactive-form remote-fcap)
+                        args))
+                    spawn-user-id))
+
+(defun crdt-M-x ()
+  (interactive)
+  (unless crdt--session
+    (error "Not a CRDT shared buffer"))
+  (let* ((table (crdt--session-remote-fcap-table crdt--session))
+         (command-symbol
+          (intern-soft
+           (completing-read "CRDT Remote Command: "
+                            (hash-table-keys table)
+                            (lambda (key) (crdt-remote-fcap-interactive-form 
(gethash key table)))
+                            t))))
+    (crdt-remote-call-interactively (crdt-get-fcap command-symbol) 
(crdt--session-local-id crdt--session))))
+
+(defun crdt--remote-command-advice (orig-func &rest args)
+  "Call remote command named ORIG-FUNC conditionally.
+Call remote command named ORIG-FUNC with ARGS,
+when such remote command is available and
+CRDT-OVERRIDE-COMMAND is non-nil."
+  (let (remote-fcap)
+    (if (and crdt-override-command crdt--session
+             (setq remote-fcap
+                   (gethash orig-func (crdt--session-remote-fcap-table 
crdt--session))))
+        (crdt-remote-apply remote-fcap args)
+      (apply orig-func args))))
 
 ;;; Buffer local variables
 
@@ -2917,27 +3028,26 @@ Use CRDT--UNINSTALL-PROCESS-ADVICES to disable those 
advices for the rescue."
       do (advice-add command :around #'crdt--org-overlay-advice))
 
 ;;;; xscheme
-(defvar crdt-xscheme-commands
-  '((xscheme-send-region (region))
-    (xscheme-send-definition (point))
-    (xscheme-send-previous-expression (point))
-    (xscheme-send-next-expression (point))
-    (xscheme-send-current-line (point))
-    (xscheme-send-buffer)
-    (xscheme-send-char)
-    (xscheme-delete-output)
-    (xscheme-send-breakpoint-interrupt)
-    (xscheme-send-proceed)
-    (xscheme-send-control-g-interrupt)
-    (xscheme-send-control-u-interrupt)
-    (xscheme-send-control-x-interrupt)
-    (scheme-debugger-self-insert (last-command-event))))
-(crdt-register-remote-commands crdt-xscheme-commands)
+(defvar crdt-xscheme-fcaps
+  (crdt-make-local-fcaps
+   '((xscheme-send-region (region))
+     (xscheme-send-definition (point))
+     (xscheme-send-previous-expression (point))
+     (xscheme-send-next-expression (point))
+     (xscheme-send-current-line (point))
+     (xscheme-send-buffer)
+     (xscheme-send-char)
+     (xscheme-delete-output)
+     (xscheme-send-breakpoint-interrupt)
+     (xscheme-send-proceed)
+     (xscheme-send-control-g-interrupt)
+     (xscheme-send-control-u-interrupt)
+     (xscheme-send-control-x-interrupt)
+     (scheme-debugger-self-insert (last-command-event)))))
 ;; xscheme doesn't use standard DEFINE-*-MODE facility
 ;; and doesn't call after-change-major-mode-hook.
 ;; Therefore we have to hack.
 (advice-add 'scheme-interaction-mode-initialize :after 
'crdt--after-change-major-mode)
-(advice-add 'scheme-interaction-mode-initialize :after 
(crdt-make-publish-command-hook crdt-xscheme-commands))
 (advice-add 'scheme-debugger-mode-initialize :after
             (lambda () ;; haxxxx!!!!
               (let ((major-mode 'scheme-debugger-mode-initialize))
@@ -2953,12 +3063,11 @@ Use CRDT--UNINSTALL-PROCESS-ADVICES to disable those 
advices for the rescue."
 (defvar comint-input-ring-size)
 (defvar comint-input-ring-file-name)
 
-(defvar crdt-comint-commands
-  '((comint-send-input (point) (point))
-    (comint-send-region (region) (region))
-    (comint-send-eof (point))))
-(crdt-register-remote-commands crdt-comint-commands)
-(add-hook 'comint-mode-hook (crdt-make-publish-command-hook 
crdt-comint-commands))
+(defvar crdt-comint-fcaps
+  (crdt-make-local-fcaps
+   '((comint-send-input (point) (point))
+     (comint-send-region (region) (region))
+     (comint-send-eof (point)))))
 
 (crdt-register-autoload 'shell-mode 'shell)
 (crdt-register-autoload 'inferior-scheme-mode 'cmuscheme)
@@ -3027,15 +3136,15 @@ This procedure is non-destructive."
 (add-hook 'crdt-mode-hook #'crdt--comint-mode-hook)
 
 ;;;; xref
-(defvar crdt-xref-commands
-  '((xref-find-definitions (point) (point))
-    (xref-find-references (point) (point))
-    (xref-show-location-at-point (point) (point))
-    (xref-pop-marker-stack () (point))
-    (xref-goto-xref (point) (point))))
-(crdt-register-remote-commands crdt-xref-commands)
-(defun crdt-xref-command-function (_user-id)
-  (mapcar #'car crdt-xref-commands))
+(defvar crdt-xref-fcaps
+  (crdt-make-local-fcaps
+   `((xref-find-definitions (point) (point window)
+                            ,(crdt-make-interactive-proxy 
'xref-find-definitions))
+     (xref-find-references (point) (point window)
+                           ,(crdt-make-interactive-proxy 
'xref-find-references))
+     (xref-show-location-at-point (point) (point))
+     (xref-pop-marker-stack () (point))
+     (xref-goto-xref (point) (point window)))))
 
 (defun crdt--xref-buffer-mode-hook ()
   (add-to-list 'crdt--enabled-text-properties 'xref-item)



reply via email to

[Prev in Thread] Current Thread [Next in Thread]