(require 'cl)
(require 'erc)
(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 (key list)
"Return value in association LIST for KEY."
`(cadr (assq ,key ,list)))
(defmacro collab-edit-set-value (key list value)
"Assign VALUE to KEY in association LIST."
`(setq (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-queue-out
nil
"Buffer local queue to hold processed change requests.")
(make-variable-buffer-local 'collab-edit-output)
(defun collab-edit-uuid ()
"Generate a UUID.
\nExample: 5ac55464-24e6-419c-99cf-5e1682bb3819"
(substring (shell-command-to-string "mcookie") 0 -1))
(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 :server collab-edit-local-irc-settings) collab-edit-irc-server)
":" (or port (collab-edit-get-value :port collab-edit-local-irc-settings) 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 :channel-prefix collab-edit-local-irc-settings) collab-edit-irc-channel-prefix)
"-" (or nick (collab-edit-get-value :nick collab-edit-local-irc-settings) 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 :server collab-edit-local-irc-settings server))
(when port (collab-edit-set-value :port collab-edit-local-irc-settings port))
(when nick (collab-edit-set-value :nick collab-edit-local-irc-settings nick))
(when password (scollab-edit-set-value :password collab-edit-local-irc-settings password))
(when name (collab-edit-set-value :name collab-edit-local-irc-settings name))
(erc :server (collab-edit-get-value :server collab-edit-local-irc-settings)
:port (collab-edit-get-value :port collab-edit-local-irc-settings)
:nick (collab-edit-get-value :nick collab-edit-local-irc-settings)
:password (collab-edit-get-value :password collab-edit-local-irc-settings)
:full-name (collab-edit-get-value :name collab-edit-local-irc-settings))
(switch-to-buffer buffer)))
(if (get-buffer server-buffer-name)
(progn
(set-buffer server-buffer-name)
(collab-edit-set-value :server collab-edit-local-irc-settings (erc-compute-server))
(collab-edit-set-value :port collab-edit-local-irc-settings (erc-compute-port))
(collab-edit-set-value :nick collab-edit-local-irc-settings (erc-compute-nick))
(collab-edit-set-value :password collab-edit-local-irc-settings (erc-compute-nick))
(collab-edit-set-value :name collab-edit-local-irc-settings (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 :channel-prefix collab-edit-local-irc-settings channel))
(unless (collab-edit-get-value :channel-prefix collab-edit-local-irc-settings)
(collab-edit-set-value :channel-prefix collab-edit-local-irc-settings 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)."
(unless buffer
(setq 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 `collab-edit-local-queue-input' and
`collab-edit-local-queue-output' queues for changes to be applied
to the curent buffer, and process them."
)
(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 beg end)))
(message (format "%S" (list ,id ,nick beg end len text)))
(collab-edit-send-message ,collab-edit-proto1-buffer-name
(format "%S" (list ,id ,nick beg 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 :nick collab-edit-local-irc-settings))
(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)