(require 'erc)
(defgroup collab-edit nil
"Collaborative editing."
:prefix "collab-edit-"
:group 'programming)
(defcustom collab-edit-irc-server
"localhost"
"IRC server to use."
:type 'string
:group 'collab-edit)
(defcustom collab-edit-irc-port
"6667"
"IRC port to use."
:type 'string
:group 'collab-edit)
(defcustom collab-edit-irc-nick
(or user-login-name
"nick")
"IRC nickname to use."
:type 'string
:group 'collab-edit)
(defcustom collab-edit-irc-password
nil
"IRC password to use."
:type 'string
:group 'collab-edit)
(defcustom collab-edit-irc-name
(or user-full-name
"Full Name")
"IRC name to use."
:type 'string
:group 'collab-edit)
(defcustom collab-edit-irc-channel-prefix
"#collab"
"IRC channel prefix to use."
:type 'string
:group 'collab-edit)
(defvar collab-edit-local-irc-settings
'((:server . nil)
(:port . nil)
(:nick . nil)
(:password . nil)
(:name . nil)
(:channel-prefix . nil))
"Buffer local IRC settings association list.")
(make-variable-buffer-local 'collab-edit-local-irc-settings)
(defmacro collab-edit-get-value (list key)
"Return value in association LIST for KEY."
`(cadr (assq ,key ,list)))
(defmacro collab-edit-set-value (list key value)
"Assign VALUE to KEY in association LIST."
`(setf (cadr (assq ,key ,list)) ,value))
(defvar collab-edit-local-irc-server-buffer-name
nil
"Buffer local irc server buffer name.")
(make-variable-buffer-local 'collab-edit-local-irc-server-buffer-name)
(defvar collab-edit-local-irc-channel-buffer-name
nil
"Buffer local irc channel buffer name.")
(make-variable-buffer-local 'collab-edit-local-irc-channel-buffer-name)
(defvar collab-edit-local-id
nil
"Buffer local unique id.")
(make-variable-buffer-local 'collab-edit-local-id)
(defvar collab-edit-local-auth
nil
"Buffer local authentication list.
\nUsers listed in this list are allowed to edit the buffer.
Everyone in the irc channel is allowed to read the buffer.")
(make-variable-buffer-local 'collab-edit-local-auth)
(defvar collab-edit-local-queue-input
nil
"Buffer local queue to hold incoming change requests.")
(make-variable-buffer-local 'collab-edit-input)
(defvar collab-edit-local-queue-output
nil
"Buffer local queue to hold processed change requests.")
(make-variable-buffer-local 'collab-edit-output)
(defun collab-edit-uuid ()
"Generate a UUID."
(replace-regexp-in-string
"^ +\\|[ \n]+$" ""
(shell-command-to-string "mcookie")))
(defun collab-edit-irc-server-buffer-name (&optional server port)
"Return generated irc server buffer name in the form of \"SERVER:PORT\".
\nSERVER and PORT are determined using the priority of optional
functions parameters first, local values second, and global
values last."
(concat (or server (collab-edit-get-value collab-edit-local-irc-settings :server) collab-edit-irc-server)
":" (or port (collab-edit-get-value collab-edit-local-irc-settings :port) collab-edit-irc-port)))
(defun collab-edit-irc-channel-buffer-name (&optional prefix nick document)
"Return generated irc channel name in the form of \"#prefix-nick-document\".
\nCHANNEL is determined using the priority of optional function
parameters first, local values second, and global values last."
(concat (or prefix (collab-edit-get-value collab-edit-local-irc-settings :channel-prefix) collab-edit-irc-channel-prefix)
"-" (or nick (collab-edit-get-value collab-edit-local-irc-settings :nick) collab-edit-irc-nick)
"-" (or document (buffer-name))))
(defun* collab-edit-connect (&key server port nick password name force)
"Connect to an irc server via `erc' if not already connected.
\nIf FORCE is non-nil, then kill existing connection and reconnect.
If any other keyword parameters are given, the corresponding
custom variable is also set."
(let ((server-buffer-name (collab-edit-irc-server-buffer-name server port))
(buffer (current-buffer)))
(save-excursion
(when (and force (get-buffer server-buffer-name))
(kill-buffer server-buffer-name))
(unless (get-buffer server-buffer-name)
(when server (collab-edit-set-value collab-edit-local-irc-settings :server server))
(when port (collab-edit-set-value collab-edit-local-irc-settings :port port))
(when nick (collab-edit-set-value collab-edit-local-irc-settings :nick nick))
(when password (collab-edit-set-value collab-edit-local-irc-settings :password password))
(when name (collab-edit-set-value collab-edit-local-irc-settings :name name))
(erc :server (collab-edit-get-value collab-edit-local-irc-settings :server)
:port (collab-edit-get-value collab-edit-local-irc-settings :port)
:nick (collab-edit-get-value collab-edit-local-irc-settings :nick)
:password (collab-edit-get-value collab-edit-local-irc-settings :password)
:full-name (collab-edit-get-value collab-edit-local-irc-settings :name))
(switch-to-buffer buffer)))
(if (get-buffer server-buffer-name)
(progn
(set-buffer server-buffer-name)
(collab-edit-set-value collab-edit-local-irc-settings :server (erc-compute-server))
(collab-edit-set-value collab-edit-local-irc-settings :port (erc-compute-port))
(collab-edit-set-value collab-edit-local-irc-settings :nick (erc-compute-nick))
(collab-edit-set-value collab-edit-local-irc-settings :password (erc-compute-nick))
(collab-edit-set-value collab-edit-local-irc-settings :name (erc-compute-full-name))
(setq collab-edit-local-irc-server-buffer-name server-buffer-name))
(error (format "Could not get irc server information from erc")))))
(defun* collab-edit-join-channel (&optional channel key &key force)
"Join CHANNEL with optional security KEY.
\nIf FORCE is non-nil, then part existing channel and rejoin."
(let ((channel-buffer-name (collab-edit-irc-channel-buffer-name channel))
(buffer (current-buffer)))
(unless (get-buffer collab-edit-local-irc-server-buffer-name)
(error "Cannot join channel until an irc server connection has been made with `collab-edit-connect'"))
(save-excursion
(when (and force (get-buffer channel-buffer-name))
(kill-buffer channel-buffer-name))
(unless (get-buffer channel-buffer-name)
(when channel (collab-edit-set-value collab-edit-local-irc-settings :channel-prefix channel))
(unless (collab-edit-get-value collab-edit-local-irc-settings :channel-prefix)
(collab-edit-set-value collab-edit-local-irc-settings :channel-prefix collab-edit-irc-channel-prefix))
(set-buffer collab-edit-local-irc-server-buffer-name)
(erc-join-channel channel-buffer-name key)
(sit-for 0.5 t)
(switch-to-buffer buffer)))
(if (get-buffer channel-buffer-name)
(setq collab-edit-local-irc-channel-buffer-name channel-buffer-name)
(error "Could not join irc channel"))))
(defun collab-edit-send-message (buffer line &optional user)
"Send LINE to the erc channel in BUFFER or to USER (if non-nil)."
(save-excursion
(if user
nil
(set-buffer buffer))
(erc-send-message line)))
(defun collab-edit-buffer (&optional buffer)
"Start collaborative editing with BUFFER (or current buffer if nil)."
(let ((buffer (or buffer (current-buffer))))
(collab-edit-connect)
(collab-edit-join-channel)
(setq collab-edit-local-id (collab-edit-uuid))
(setq collab-edit-local-auth nil)
(setq collab-edit-local-queue-input nil)
(setq collab-edit-local-queue-output nil)
))
(defun collab-edit-process-queues ()
"Check queues for changes to be applied to the curent buffer, and process them.
\nQueues are stored in `collab-edit-local-queue-input' and
`collab-edit-local-queue-output' "
)
(defvar collab-edit-proto1-buffer-name
"*Collab-Edit-Change-Report*"
"Buffer name to use for prototype 1.")
(defun collab-edit-proto1 ()
"Prototype 1."
(let ((nick "test")
id
buffer)
(setq id (collab-edit-uuid))
(setq buffer (generate-new-buffer collab-edit-proto1-buffer-name))
(add-to-list 'after-change-functions
`(lambda (beg end len)
(let ((text (buffer-substring begin end)))
(message (format "%S" (list ,id ,nick begin end len text)))
(collab-edit-send-message ,collab-edit-proto1-buffer-name
(format "%S" (list ,id ,nick begin end len text))))))))
(defun collab-edit-proto2 ()
"Prototype 2."
(collab-edit-connect :server "localhost" :port "6667")
(collab-edit-join-channel)
(let ((nick (collab-edit-get-value collab-edit-local-irc-settings :nick)))
(setq collab-edit-local-id (collab-edit-uuid))
(message (concat "collab-edit-local-irc-channel-buffer-name: " collab-edit-local-irc-channel-buffer-name))
(message (concat "collab-edit-local-id: " collab-edit-local-id))
(message (concat "collab-edit-local-irc-nick: " nick))
(add-to-list 'after-change-functions
`(lambda (beg end len)
(let ((text (buffer-substring beg end)))
(message (format "%S" (list ,collab-edit-local-id ,nick beg end len text)))
(collab-edit-send-message ,collab-edit-local-irc-channel-buffer-name
(format "%S" (list ,collab-edit-local-id ,nick beg end len text))))))))
(provide 'collab-edit)