Added context buttons.
[lurk.git] / murk.el
diff --git a/murk.el b/murk.el
index 0766b7d..b438aa3 100644 (file)
--- a/murk.el
+++ b/murk.el
@@ -1,4 +1,4 @@
-;;; murk.el --- Multiserver Unibuffer iRc Klient -*- lexical-binding:t -*-
+;;; murk.el --- Multinetwork Unibuffer iRc Klient -*- lexical-binding:t -*-
 
 ;; Copyright (C) 2024 plugd
 
 
 ;; Copyright (C) 2024 plugd
 
@@ -26,7 +26,7 @@
 
 ;;; Commentary:
 
 
 ;;; Commentary:
 
-;; A very simple IRC server which uses only a single buffer.
+;; A very simple IRC client which uses only a single buffer.
 
 ;;; Code:
 
 
 ;;; Code:
 
@@ -38,7 +38,7 @@
 ;;; Customizations
 
 (defgroup murk nil
 ;;; Customizations
 
 (defgroup murk nil
-  "Multiserver Unibuffer iRc Klient"
+  "Multinetwork Unibuffer iRc Klient"
   :group 'network)
 
 (defcustom murk-default-nick "plugd"
   :group 'network)
 
 (defcustom murk-default-nick "plugd"
@@ -147,61 +147,61 @@ in which case they match anything."
 ;;
 
 (defvar murk-connection-table nil
 ;;
 
 (defvar murk-connection-table nil
-  "An alist associating servers to connection information.
+  "An alist associating networks to connection information.
 This includes the process and the response string.")
 
 This includes the process and the response string.")
 
-(defun murk-connection-process (server)
-  (elt (assoc server murk-connection-table) 1))
+(defun murk-connection-process (network)
+  (elt (assoc network murk-connection-table) 1))
 
 
-(defun murk-connection-nick (server)
-  (elt (assoc server murk-connection-table) 2))
+(defun murk-connection-nick (network)
+  (elt (assoc network murk-connection-table) 2))
 
 
-(defun murk-set-connection-nick (server nick)
-  (setf (elt (assoc server murk-connection-table) 2) nick))
+(defun murk-set-connection-nick (network nick)
+  (setf (elt (assoc network murk-connection-table) 2) nick))
 
 
-(defun murk-connection-response (server)
-  (elt (assoc server murk-connection-table) 3))
+(defun murk-connection-response (network)
+  (elt (assoc network murk-connection-table) 3))
 
 
-(defun murk-set-connection-response (server string)
-  (setf (elt (assoc server murk-connection-table) 3) string))
+(defun murk-set-connection-response (network string)
+  (setf (elt (assoc network murk-connection-table) 3) string))
 
 
-(defun murk-connection-new (server process nick)
+(defun murk-connection-new (network process nick)
   (add-to-list 'murk-connection-table
   (add-to-list 'murk-connection-table
-               (list server process nick "")))
+               (list network process nick "")))
 
 
-(defun murk-connection-remove (server)
+(defun murk-connection-remove (network)
   (setq murk-connection-table
   (setq murk-connection-table
-        (seq-remove (lambda (row) (equal (car row) server))
+        (seq-remove (lambda (row) (equal (car row) network))
                     murk-connection-table)))
 
                     murk-connection-table)))
 
-(defun murk-make-server-filter (server)
+(defun murk-make-network-filter (network)
   (lambda (_proc string)
   (lambda (_proc string)
-    (dolist (line (split-string (concat (murk-connection-response server) string)
+    (dolist (line (split-string (concat (murk-connection-response network) string)
                                 "\n"))
       (if (string-suffix-p "\r" line)
                                 "\n"))
       (if (string-suffix-p "\r" line)
-          (murk-eval-msg-string server (string-trim line))
-        (murk-set-connection-response server line)))))
+          (murk-eval-msg-string network (string-trim line))
+        (murk-set-connection-response network line)))))
 
 
-(defun murk-make-server-sentinel (server)
+(defun murk-make-network-sentinel (network)
   (lambda (_proc string)
     (unless (equal "open" (string-trim string))
   (lambda (_proc string)
     (unless (equal "open" (string-trim string))
-      (murk-display-error "Disconnected from server.")
-      (murk-connection-remove server)
-      (murk-remove-server-contexts server)
+      (murk-display-error "Disconnected from network.")
+      (murk-connection-remove network)
+      (murk-remove-network-contexts network)
       (murk-highlight-current-context)
       (murk-render-prompt))))
 
       (murk-highlight-current-context)
       (murk-render-prompt))))
 
-(defun murk-start-process (server)
-  (let* ((row (assoc server murk-networks))
+(defun murk-start-process (network)
+  (let* ((row (assoc network murk-networks))
          (host (elt row 1))
          (port (elt row 2))
          (flags (seq-drop row 3)))
          (host (elt row 1))
          (port (elt row 2))
          (flags (seq-drop row 3)))
-    (make-network-process :name (concat "murk-" server)
+    (make-network-process :name (concat "murk-" network)
                           :host host
                           :service port
                           :family nil
                           :host host
                           :service port
                           :family nil
-                          :filter (murk-make-server-filter server)
-                          :sentinel (murk-make-server-sentinel server)
+                          :filter (murk-make-network-filter network)
+                          :sentinel (murk-make-network-sentinel network)
                           :nowait nil
                           :tls-parameters (if (memq :notls flags)
                                               nil
                           :nowait nil
                           :tls-parameters (if (memq :notls flags)
                                               nil
@@ -215,29 +215,29 @@ This includes the process and the response string.")
 
 ;; IDEA: Have a single ping timer which pings all connected hosts
 
 
 ;; IDEA: Have a single ping timer which pings all connected hosts
 
-(defun murk-connect (server)
-  (if (assoc server murk-connection-table)
+(defun murk-connect (network)
+  (if (assoc network murk-connection-table)
       (murk-display-error "Already connected to this network")
       (murk-display-error "Already connected to this network")
-    (if (not (assoc server murk-networks))
-        (murk-display-error "Network '" server "' is unknown.")
-      (let ((proc (murk-start-process server)))
-        (murk-connection-new server proc murk-default-nick))
-      (murk-send-msg server (murk-msg nil nil "USER" murk-default-nick 0 "*" murk-default-nick))
-      (murk-send-msg server (murk-msg nil nil "NICK" murk-default-nick))
-      (murk-add-context (list server))
+    (if (not (assoc network murk-networks))
+        (murk-display-error "Network '" network "' is unknown.")
+      (let ((proc (murk-start-process network)))
+        (murk-connection-new network proc murk-default-nick))
+      (murk-send-msg network (murk-msg nil nil "USER" murk-default-nick 0 "*" murk-default-nick))
+      (murk-send-msg network (murk-msg nil nil "NICK" murk-default-nick))
+      (murk-add-context (list network))
       (murk-highlight-current-context)
       (murk-render-prompt))))
 
       (murk-highlight-current-context)
       (murk-render-prompt))))
 
-(defun murk-send-msg (server msg)
+(defun murk-send-msg (network msg)
   (if murk-debug
       (murk-display-string nil nil (murk-msg->string msg)))
   (if murk-debug
       (murk-display-string nil nil (murk-msg->string msg)))
-  (let ((proc (murk-connection-process server)))
+  (let ((proc (murk-connection-process network)))
     (if (and proc (eq (process-status proc) 'open))
         (process-send-string proc (concat (murk-msg->string msg) "\r\n"))
     (if (and proc (eq (process-status proc) 'open))
         (process-send-string proc (concat (murk-msg->string msg) "\r\n"))
-      (murk-display-error "No server connection established"))))
+      (murk-display-error "No network connection established"))))
 
 
 
 
-;;; Server messages
+;;; network messages
 ;;
 
 (defun murk-msg (tags src cmd &rest params)
 ;;
 
 (defun murk-msg (tags src cmd &rest params)
@@ -308,12 +308,12 @@ portion of the source component of the message, as mURK doesn't use this.")
 ;;; Contexts
 ;;
 
 ;;; Contexts
 ;;
 
-;; A context is a list (server channel users) identifying the server
+;; A context is a list (network channel) identifying the network
 ;; and channel.  The tail of the list contains the nicks of users
 ;; present in the channel.
 ;;
 ;; and channel.  The tail of the list contains the nicks of users
 ;; present in the channel.
 ;;
-;; Each server has a special context (server) used for messages
-;; to/from the server itself.
+;; Each network has a special context (network) used for messages
+;; to/from the network itself.
 
 (defvar murk-contexts nil
   "List of currently-available contexts.
 
 (defvar murk-contexts nil
   "List of currently-available contexts.
@@ -326,27 +326,21 @@ The head of this list is always the current context.")
     nil))
 
 (defun murk-contexts-equal (c1 c2)
     nil))
 
 (defun murk-contexts-equal (c1 c2)
-  (if (murk-server-context-p c1)
-      (and (murk-server-context-p c2)
-           (equal (murk-context-server c1)
-                  (murk-context-server c2)))
-    (and (not (murk-server-context-p c2))
+  (if (murk-network-context-p c1)
+      (and (murk-network-context-p c2)
+           (equal (murk-context-network c1)
+                  (murk-context-network c2)))
+    (and (not (murk-network-context-p c2))
          (equal (seq-take c1 2)
                 (seq-take c2 2)))))
 
          (equal (seq-take c1 2)
                 (seq-take c2 2)))))
 
-(defun murk-context-server (ctx)
+(defun murk-context-network (ctx)
   (elt ctx 0))
 
 (defun murk-context-channel (ctx)
   (elt ctx 1))
 
   (elt ctx 0))
 
 (defun murk-context-channel (ctx)
   (elt ctx 1))
 
-(defun murk-context-users (ctx)
-  (elt ctx 2))
-
-(defun murk-set-context-users (ctx users)
-  (setcar (cddr ctx) users))
-
-(defun murk-server-context-p (ctx)
+(defun murk-network-context-p (ctx)
   (not (cdr ctx)))
 
 (defun murk-add-context (ctx)
   (not (cdr ctx)))
 
 (defun murk-add-context (ctx)
@@ -359,32 +353,33 @@ The head of this list is always the current context.")
            (murk-contexts-equal this-ctx ctx))
          murk-contexts)))
 
            (murk-contexts-equal this-ctx ctx))
          murk-contexts)))
 
-(defun murk-remove-server-contexts (server)
+(defun murk-remove-network-contexts (network)
   (setq murk-contexts
   (setq murk-contexts
-        (seq-remove (lambda (row) (equal (car row) server))
+        (seq-remove (lambda (row) (equal (car row) network))
                     murk-contexts)))
 
 (defun murk-context->string (ctx)
                     murk-contexts)))
 
 (defun murk-context->string (ctx)
-   (if (murk-server-context-p ctx)
-       (concat "[" (murk-context-server ctx) "]")
-     (concat (murk-context-channel ctx) "@"
-             (murk-context-server ctx))))
+  (concat
+   (if (murk-network-context-p ctx)
+       ""
+     (concat (murk-context-channel ctx) "@"))
+   (murk-context-network ctx)))
 
 (defun murk-string->context (string)
   (if (not (string-prefix-p "#" string))
       (murk-get-context string)
     (let* ((parts (string-split string "@"))
            (channel (elt parts 0))
 
 (defun murk-string->context (string)
   (if (not (string-prefix-p "#" string))
       (murk-get-context string)
     (let* ((parts (string-split string "@"))
            (channel (elt parts 0))
-           (server (elt parts 1)))
-      (murk-get-context server channel))))
+           (network (elt parts 1)))
+      (murk-get-context network channel))))
 
 
-(defun murk-get-context (server &optional channel)
+(defun murk-get-context (network &optional channel)
   (if (and channel (string-prefix-p "#" channel))
   (if (and channel (string-prefix-p "#" channel))
-      (let ((test-ctx (list server channel)))
+      (let ((test-ctx (list network channel)))
         (seq-find (lambda (ctx)
                     (equal (seq-take ctx 2) test-ctx))
                   murk-contexts))
         (seq-find (lambda (ctx)
                     (equal (seq-take ctx 2) test-ctx))
                   murk-contexts))
-    (car (member (list server) murk-contexts))))
+    (car (member (list network) murk-contexts))))
 
 (defun murk-cycle-contexts (&optional reverse)
   (setq murk-contexts
 
 (defun murk-cycle-contexts (&optional reverse)
   (setq murk-contexts
@@ -399,32 +394,56 @@ The head of this list is always the current context.")
   (setq murk-contexts
         (let* ((new-head (memq ctx murk-contexts))
                (new-tail (take (- (length murk-contexts)
   (setq murk-contexts
         (let* ((new-head (memq ctx murk-contexts))
                (new-tail (take (- (length murk-contexts)
-                                  (length new-head)))))
+                                  (length new-head))
+                               murk-contexts)))
           (append new-head new-tail))))
 
           (append new-head new-tail))))
 
+;;; Context users
+;;
+
+(defvar murk-context-users nil
+  "Association list between channel contexts and users.")
+
+(defun murk-get-context-users (ctx)
+  (cdr (assoc ctx murk-context-users)))
+
+(defun murk-set-context-users (ctx users)
+  (setq murk-context-users
+        (cons (cons ctx users) (assoc-delete-all ctx murk-context-users))))
+
 (defun murk-add-context-users (ctx users)
   (murk-set-context-users
    ctx
 (defun murk-add-context-users (ctx users)
   (murk-set-context-users
    ctx
-   (cl-union users (murk-context-users ctx))))
+   (cl-union users (murk-get-context-users ctx))))
 
 (defun murk-del-context-user (ctx user)
   (murk-set-context-users
    ctx
 
 (defun murk-del-context-user (ctx user)
   (murk-set-context-users
    ctx
-   (delete user (murk-context-users ctx))))
+   (delete user (murk-get-context-users ctx))))
+
+(defun murk-del-all-context-users (ctx)
+  (murk-set-context-users ctx nil))
 
 
-(defun murk-del-server-user (server user)
+(defun murk-del-network-user (network user)
   (dolist (ctx murk-contexts)
   (dolist (ctx murk-contexts)
-    (if (and (equal (murk-context-server ctx) server)
-             (not (murk-server-context-p ctx)))
+    (if (and (equal (murk-context-network ctx) network)
+             (not (murk-network-context-p ctx)))
         (murk-del-context-user ctx user))))
 
         (murk-del-context-user ctx user))))
 
-(defun murk-rename-server-user (server old-nick new-nick)
+(defun murk-del-all-network-users (network)
+  (dolist (ctx murk-contexts)
+    (if (and (equal (murk-context-network ctx) network)
+             (not (murk-network-context-p ctx)))
+        (murk-del-all-context-users ctx))))
+
+(defun murk-rename-network-user (network old-nick new-nick)
   (dolist (ctx murk-contexts)
   (dolist (ctx murk-contexts)
-    (when (and (equal (murk-context-server ctx) server)
-               (member old-nick (murk-context-users ctx)))
+    (when (and (equal (murk-context-network ctx) network)
+               (member old-nick (murk-get-context-users ctx)))
       (murk-del-context-user ctx old-nick)
       (murk-add-context-users ctx (list new-nick)))))
 
       (murk-del-context-user ctx old-nick)
       (murk-add-context-users ctx (list new-nick)))))
 
+
 ;;; Buffer
 ;;
 
 ;;; Buffer
 ;;
 
@@ -473,17 +492,17 @@ The head of this list is always the current context.")
                 '((:eval
                    (let* ((ctx (murk-current-context)))
                      (if ctx
                 '((:eval
                    (let* ((ctx (murk-current-context)))
                      (if ctx
-                         (let ((server (murk-context-server ctx)))
+                         (let ((network (murk-context-network ctx)))
                            (concat
                            (concat
-                            "Network: " server ", "
-                            (if (murk-server-context-p ctx)
-                                "Server"
+                            "Network: " network ", "
+                            (if (murk-network-context-p ctx)
+                                "network"
                               (concat
                                "Channel: "
                                (murk-context-channel ctx)
                                " ("
                                (number-to-string
                               (concat
                                "Channel: "
                                (murk-context-channel ctx)
                                " ("
                                (number-to-string
-                                (length (murk-context-users ctx)))
+                                (length (murk-get-context-users ctx)))
                                ")"))))
                        "No connection")))))))
 
                                ")"))))
                        "No connection")))))))
 
@@ -531,11 +550,10 @@ The head of this list is always the current context.")
   "List of seen contexts and associated face lists.")
 
 (defun murk-get-context-facelist (context)
   "List of seen contexts and associated face lists.")
 
 (defun murk-get-context-facelist (context)
-  (let* ((short-ctx (take 2 context))
-         (facelist (gethash short-ctx murk-context-facelists)))
+  (let* ((facelist (gethash context murk-context-facelists)))
     (unless facelist
       (setq facelist (list 'murk-text))
     (unless facelist
       (setq facelist (list 'murk-text))
-      (puthash short-ctx facelist murk-context-facelists))
+      (puthash context facelist murk-context-facelists))
     facelist))
 
 (defun murk--fill-strings (col indent &rest strings)
     facelist))
 
 (defun murk--fill-strings (col indent &rest strings)
@@ -555,11 +573,10 @@ The head of this list is always the current context.")
              (old-pos (marker-position murk-prompt-marker))
              (padded-timestamp (concat (format-time-string "%H:%M ")))
              (padded-prefix (if prefix (concat prefix " ") ""))
              (old-pos (marker-position murk-prompt-marker))
              (padded-timestamp (concat (format-time-string "%H:%M ")))
              (padded-prefix (if prefix (concat prefix " ") ""))
-             (short-ctx (take 2 context))
-             (context-atom (if short-ctx
-                               (intern (murk-context->string short-ctx))
+             (context-atom (if context
+                               (intern (murk-context->string context))
                              nil))
                              nil))
-             (context-face (murk-get-context-facelist short-ctx)))
+             (context-face (murk-get-context-facelist context)))
         (insert-before-markers
          (murk--fill-strings
           80
         (insert-before-markers
          (murk--fill-strings
           80
@@ -568,37 +585,56 @@ The head of this list is always the current context.")
           (propertize padded-timestamp
                       'face 'murk-timestamp
                       'read-only t
           (propertize padded-timestamp
                       'face 'murk-timestamp
                       'read-only t
-                      'context short-ctx
+                      'context context
                       'invisible context-atom)
           (propertize padded-prefix
                       'read-only t
                       'invisible context-atom)
           (propertize padded-prefix
                       'read-only t
-                      'context short-ctx
+                      'context context
                       'invisible context-atom)
           (murk-add-formatting
            (propertize (concat (apply #'murk-buttonify-urls strings) "\n")
                        'face context-face
                        'read-only t
                       'invisible context-atom)
           (murk-add-formatting
            (propertize (concat (apply #'murk-buttonify-urls strings) "\n")
                        'face context-face
                        'read-only t
-                       'context short-ctx
+                       'context context
                        'invisible context-atom)))))))
   (murk-scroll-windows-to-last-line))
 
                        'invisible context-atom)))))))
   (murk-scroll-windows-to-last-line))
 
-(defun murk-display-message (server from to text)
+(defun murk-click-context (button)
+  (murk-switch-to-context (button-get button 'context))
+  (murk-highlight-current-context)
+  (murk-render-prompt)
+  (if murk-zoomed
+      (murk-zoom-in (murk-current-context))))
+
+(defun murk-make-context-button (context &optional string)
+  (with-temp-buffer
+    (let ((label (or string (murk-context->string context))))
+      (insert-text-button label
+                          'action #'murk-click-context
+                          'context context
+                          'follow-link t
+                          'help-echo "Switch context"))
+    (buffer-string)))
+
+(defun murk-display-message (network from to text)
   (let ((context (if (string-prefix-p "#" to)
   (let ((context (if (string-prefix-p "#" to)
-                     (murk-get-context server to)
-                   (murk-get-context server))))
+                     (murk-get-context network to)
+                   (murk-get-context network))))
     (murk-display-string
      context
      (propertize
     (murk-display-string
      context
      (propertize
-      (if (murk-server-context-p context)
+      (if (murk-network-context-p context)
           (concat "[" from "->" to "]")
           (concat "[" from "->" to "]")
-        (concat (murk-context->string context) " <" from ">"))
+        (concat
+         (murk-make-context-button context)
+         " <" from ">"))
       'face (murk-get-context-facelist context))
      text)))
 
       'face (murk-get-context-facelist context))
      text)))
 
-(defun murk-display-action (server from to action-text)
+(defun murk-display-action (network from to action-text)
   (let ((context (if (string-prefix-p "#" to)
   (let ((context (if (string-prefix-p "#" to)
-                     (murk-get-context server to)
-                   (murk-get-context server))))
+                     (murk-get-context network to)
+                   (murk-get-context network))))
     (murk-display-string
      context
      (propertize
     (murk-display-string
      context
      (propertize
@@ -619,12 +655,13 @@ The head of this list is always the current context.")
    (apply #'concat messages)))
 
 (defun murk-highlight-current-context ()
    (apply #'concat messages)))
 
 (defun murk-highlight-current-context ()
-  (maphash
-   (lambda (this-context facelist)
-     (if (equal (take 2 this-context) (take 2 (murk-current-context)))
-         (setcar facelist 'murk-text)
-       (setcar facelist 'murk-faded)))
-   murk-context-facelists)
+  (with-current-buffer "*murk*"
+    (maphash
+     (lambda (this-context facelist)
+       (if (equal this-context (murk-current-context))
+           (setcar facelist 'murk-text)
+         (setcar facelist 'murk-faded)))
+     murk-context-facelists))
   (force-window-update "*murk*"))
 
 (defun murk-zoom-in (context)
   (force-window-update "*murk*"))
 
 (defun murk-zoom-in (context)
@@ -634,7 +671,7 @@ The head of this list is always the current context.")
        (when this-context
          (let ((this-context-atom
                 (intern (murk-context->string this-context))))
        (when this-context
          (let ((this-context-atom
                 (intern (murk-context->string this-context))))
-           (if (equal this-context (take 2 context))
+           (if (equal this-context context)
                (remove-from-invisibility-spec this-context-atom)
              (add-to-invisibility-spec this-context-atom)))))
      murk-context-facelists)
                (remove-from-invisibility-spec this-context-atom)
              (add-to-invisibility-spec this-context-atom)))))
      murk-context-facelists)
@@ -739,14 +776,14 @@ The head of this list is always the current context.")
 ;;; Message evaluation
 ;;
 
 ;;; Message evaluation
 ;;
 
-(defun murk-eval-msg-string (server string)
+(defun murk-eval-msg-string (network string)
   (if murk-debug
       (murk-display-string nil nil string))
   (let* ((msg (murk-string->msg string)))
   (if murk-debug
       (murk-display-string nil nil string))
   (let* ((msg (murk-string->msg string)))
-    (murk-process-autoreplies server msg)
+    (murk-process-autoreplies network msg)
     (pcase (murk-msg-cmd msg)
       ("PING"
     (pcase (murk-msg-cmd msg)
       ("PING"
-       (murk-send-msg server
+       (murk-send-msg network
         (murk-msg nil nil "PONG" (murk-msg-params msg))))
 
       ("PONG")
         (murk-msg nil nil "PONG" (murk-msg-params msg))))
 
       ("PONG")
@@ -755,115 +792,127 @@ The head of this list is always the current context.")
        (let* ((params (murk-msg-params msg))
               (nick (elt params 0))
               (text (string-join (seq-drop params 1) " ")))
        (let* ((params (murk-msg-params msg))
               (nick (elt params 0))
               (text (string-join (seq-drop params 1) " ")))
-         (murk-set-connection-nick server nick)
-         (murk-display-notice (murk-get-context server) text)))
+         (murk-set-connection-nick network nick)
+         (murk-display-notice (murk-get-context network) text))
+       (let* ((row (assoc network murk-networks))
+              (channels (if (memq :channels row)
+                            (cdr (memq :channels row))
+                          nil)))
+         (dolist (channel channels)
+           (murk-command-join (list channel)))))
 
       ("353" ; NAMEREPLY
        (let* ((params (murk-msg-params msg))
               (channel (elt params 2))
               (names (split-string (elt params 3)))
 
       ("353" ; NAMEREPLY
        (let* ((params (murk-msg-params msg))
               (channel (elt params 2))
               (names (split-string (elt params 3)))
-              (ctx (murk-get-context server channel)))
+              (ctx (murk-get-context network channel)))
          (if ctx
              (murk-add-context-users ctx names)
          (if ctx
              (murk-add-context-users ctx names)
-           (murk-display-notice nil "Users in " channel
+           (murk-display-notice ctx "Users in " channel
                                 ": " (string-join names " ")))))
 
       ("366" ; ENDOFNAMES
        (let* ((params (murk-msg-params msg))
               (channel (elt params 1))
                                 ": " (string-join names " ")))))
 
       ("366" ; ENDOFNAMES
        (let* ((params (murk-msg-params msg))
               (channel (elt params 1))
-              (ctx (murk-get-context server channel)))
+              (ctx (murk-get-context network channel)))
          (if ctx
              (murk-display-notice
               ctx
          (if ctx
              (murk-display-notice
               ctx
-              (murk--as-string (length (murk-context-users ctx)))
+              (murk--as-string (length (murk-get-context-users ctx)))
               " users in " channel)
               " users in " channel)
-           (murk-display-notice nil "End of " channel " names list."))))
+           (murk-display-notice (murk-get-context network)
+                                "End of " channel " names list."))))
 
       ("331" ; RPL_NOTOPIC
        (let* ((params (murk-msg-params msg))
               (channel (elt params 1))
 
       ("331" ; RPL_NOTOPIC
        (let* ((params (murk-msg-params msg))
               (channel (elt params 1))
-              (ctx (murk-get-context server channel)))
+              (ctx (murk-get-context network channel)))
          (murk-display-notice ctx "No topic set.")))
 
       ("332" ; RPL_TOPIC
        (let* ((params (murk-msg-params msg))
               (channel (elt params 1))
               (topic (elt params 2))
          (murk-display-notice ctx "No topic set.")))
 
       ("332" ; RPL_TOPIC
        (let* ((params (murk-msg-params msg))
               (channel (elt params 1))
               (topic (elt params 2))
-              (ctx (murk-get-context server channel)))
+              (ctx (murk-get-context network channel)))
          (murk-display-notice ctx "Topic: " topic)))
 
       ((rx (= 3 (any digit)))
          (murk-display-notice ctx "Topic: " topic)))
 
       ((rx (= 3 (any digit)))
-       (murk-display-notice (murk-get-context server)
+       (murk-display-notice (murk-get-context network)
                             (mapconcat 'identity (cdr (murk-msg-params msg)) " ")))
 
       ((and "JOIN"
                             (mapconcat 'identity (cdr (murk-msg-params msg)) " ")))
 
       ((and "JOIN"
-            (guard (equal (murk-connection-nick server)
+            (guard (equal (murk-connection-nick network)
                           (murk-msg-src msg))))
                           (murk-msg-src msg))))
-       (let ((channel (car (murk-msg-params msg))))
-         (murk-add-context (list server channel nil))
+       (let* ((channel (car (murk-msg-params msg)))
+              (context (list network channel)))
+         (murk-add-context context)
+         (murk-del-all-context-users context)
          (murk-display-notice (murk-current-context)
          (murk-display-notice (murk-current-context)
-                              "Joining channel " channel " on " server)
+                              "Joining channel " channel " on " network)
          (murk-highlight-current-context)
          (murk-render-prompt)))
 
       ("JOIN"
        (let* ((channel (car (murk-msg-params msg)))
               (nick (murk-msg-src msg))
          (murk-highlight-current-context)
          (murk-render-prompt)))
 
       ("JOIN"
        (let* ((channel (car (murk-msg-params msg)))
               (nick (murk-msg-src msg))
-              (ctx (murk-get-context server channel)))
+              (ctx (murk-get-context network channel)))
          (murk-add-context-users ctx (list nick))
          (if murk-show-joins
              (murk-display-notice ctx nick " joined channel " channel
          (murk-add-context-users ctx (list nick))
          (if murk-show-joins
              (murk-display-notice ctx nick " joined channel " channel
-                                  " on " server))))
+                                  " on " network))))
 
       ((and "PART"
 
       ((and "PART"
-            (guard (equal (murk-connection-nick server)
+            (guard (equal (murk-connection-nick network)
                           (murk-msg-src msg))))
                           (murk-msg-src msg))))
-       (let ((channel (car (murk-msg-params msg))))
-         (murk-display-notice (murk-current-context) "Left channel " channel)
-         (murk-remove-context (list server channel))
+       (let* ((channel (car (murk-msg-params msg)))
+              (context (list network channel)))
+         (murk-display-notice context "Left channel " channel)
+         (murk-remove-context context)
+         (murk-del-all-context-users context)
          (murk-highlight-current-context)
          (murk-render-prompt)))
 
       ("PART"
        (let* ((channel (car (murk-msg-params msg)))
               (nick (murk-msg-src msg))
          (murk-highlight-current-context)
          (murk-render-prompt)))
 
       ("PART"
        (let* ((channel (car (murk-msg-params msg)))
               (nick (murk-msg-src msg))
-              (ctx (murk-get-context server channel)))
+              (ctx (murk-get-context network channel)))
          (murk-del-context-user ctx nick)
          (if murk-show-joins
              (murk-display-notice ctx nick " left channel " channel
          (murk-del-context-user ctx nick)
          (if murk-show-joins
              (murk-display-notice ctx nick " left channel " channel
-                                  " on " server))))
+                                  " on " network))))
 
       ((and "NICK"
 
       ((and "NICK"
-            (guard (equal (murk-connection-nick server)
+            (guard (equal (murk-connection-nick network)
                           (murk-msg-src msg))))
        (let ((new-nick (car (murk-msg-params msg)))
                           (murk-msg-src msg))))
        (let ((new-nick (car (murk-msg-params msg)))
-             (old-nick (murk-connection-nick server)))
-         (murk-set-connection-nick server new-nick)
-         (murk-rename-server-user server old-nick new-nick)
-         (murk-display-notice (murk-get-context server)
-                              "Nick set to " new-nick " on " server)))
+             (old-nick (murk-connection-nick network)))
+         (murk-set-connection-nick network new-nick)
+         (murk-rename-network-user network old-nick new-nick)
+         (murk-display-notice (murk-get-context network)
+                              "Nick set to " new-nick " on " network)))
 
       ("NICK"
        (let ((old-nick (murk-msg-src msg))
              (new-nick (car (murk-msg-params msg))))
 
       ("NICK"
        (let ((old-nick (murk-msg-src msg))
              (new-nick (car (murk-msg-params msg))))
-         (murk-display-notice nil old-nick " is now known as " new-nick
-                              " on " server)
-         (murk-rename-server-user server old-nick new-nick)))
+         (murk-display-notice (murk-get-context network)
+                              old-nick " is now known as " new-nick
+                              " on " network)
+         (murk-rename-network-user network old-nick new-nick)))
 
       ("TOPIC"
        (let ((channel (car (murk-msg-params msg)))
              (nick (murk-msg-src msg))
              (topic (cadr (murk-msg-params msg))))
 
       ("TOPIC"
        (let ((channel (car (murk-msg-params msg)))
              (nick (murk-msg-src msg))
              (topic (cadr (murk-msg-params msg))))
-         (murk-display-notice (murk-get-context server channel)
+         (murk-display-notice (murk-get-context network channel)
                               nick " set the topic: " topic)))
 
       ("QUIT"
        (let ((nick (murk-msg-src msg))
              (reason (mapconcat 'identity (murk-msg-params msg) " ")))
                               nick " set the topic: " topic)))
 
       ("QUIT"
        (let ((nick (murk-msg-src msg))
              (reason (mapconcat 'identity (murk-msg-params msg) " ")))
-         (murk-del-server-user server nick)
+         (murk-del-network-user network nick)
          (if murk-show-joins
          (if murk-show-joins
-             (murk-display-notice (murk-get-context server)
-                                  nick " quit: " reason))))
+             (murk-display-notice (murk-get-context network)
+                                  nick " on " network " has quit: " reason))))
 
       ("NOTICE"
        (let ((nick (murk-msg-src msg))
 
       ("NOTICE"
        (let ((nick (murk-msg-src msg))
@@ -873,10 +922,10 @@ The head of this list is always the current context.")
            ((rx (: "\01VERSION "
                    (let version (* (not "\01")))
                    "\01"))
            ((rx (: "\01VERSION "
                    (let version (* (not "\01")))
                    "\01"))
-            (murk-display-notice (murk-get-context server)
+            (murk-display-notice (murk-get-context network)
                                  "CTCP version reply from " nick ": " version))
            (_
                                  "CTCP version reply from " nick ": " version))
            (_
-            (murk-display-notice (murk-get-context server channel) text)))))
+            (murk-display-notice (murk-get-context network channel) text)))))
 
       ("PRIVMSG"
        (let* ((from (murk-msg-src msg))
 
       ("PRIVMSG"
        (let* ((from (murk-msg-src msg))
@@ -886,38 +935,38 @@ The head of this list is always the current context.")
          (pcase text
            ("\01VERSION\01"
             (let ((version-string (concat murk-version " - running on GNU Emacs " emacs-version)))
          (pcase text
            ("\01VERSION\01"
             (let ((version-string (concat murk-version " - running on GNU Emacs " emacs-version)))
-              (murk-send-msg server
+              (murk-send-msg network
                              (murk-msg nil nil "NOTICE"
                                        (list from (concat "\01VERSION "
                                                           version-string
                                                           "\01")))))
                              (murk-msg nil nil "NOTICE"
                                        (list from (concat "\01VERSION "
                                                           version-string
                                                           "\01")))))
-            (murk-display-notice (murk-get-context server)
+            (murk-display-notice (murk-get-context network)
                                  "CTCP version request received from "
                                  "CTCP version request received from "
-                                 from " on " server))
+                                 from " on " network))
 
            ((rx (let ping (: "\01PING " (* (not "\01")) "\01")))
 
            ((rx (let ping (: "\01PING " (* (not "\01")) "\01")))
-            (murk-send-msg server (murk-msg nil nil "NOTICE" (list from ping)))
-            (murk-display-notice (murk-get-context server)
-                                 "CTCP ping received from " from " on " server))
+            (murk-send-msg network (murk-msg nil nil "NOTICE" (list from ping)))
+            (murk-display-notice (murk-get-context network)
+                                 "CTCP ping received from " from " on " network))
 
            ("\01USERINFO\01"
 
            ("\01USERINFO\01"
-            (murk-display-notice (murk-get-context server)
+            (murk-display-notice (murk-get-context network)
                                  "CTCP userinfo request from " from
                                  "CTCP userinfo request from " from
-                                 " on " server " (no response sent)"))
+                                 " on " network " (no response sent)"))
 
            ("\01CLIENTINFO\01"
 
            ("\01CLIENTINFO\01"
-            (murk-display-notice (murk-get-context server)
+            (murk-display-notice (murk-get-context network)
                                  "CTCP clientinfo request from " from
                                  "CTCP clientinfo request from " from
-                                 " on " server " (no response sent)"))
+                                 " on " network " (no response sent)"))
 
            ((rx (: "\01ACTION " (let action-text (* (not "\01"))) "\01"))
 
            ((rx (: "\01ACTION " (let action-text (* (not "\01"))) "\01"))
-            (murk-display-action server from to action-text))
+            (murk-display-action network from to action-text))
 
            (_
 
            (_
-            (murk-display-message server from to text)))))
+            (murk-display-message network from to text)))))
 
       (_
 
       (_
-       (murk-display-notice (murk-get-context server)
+       (murk-display-notice (murk-get-context network)
                             (murk-msg->string msg))))))
 
 
                             (murk-msg->string msg))))))
 
 
@@ -932,23 +981,23 @@ The head of this list is always the current context.")
           nil)
       t))
 
           nil)
       t))
 
-(defun murk-process-autoreply (server msg autoreply)
+(defun murk-process-autoreply (network msg autoreply)
   (let ((matcher (car autoreply))
         (reply (cadr autoreply)))
   (let ((matcher (car autoreply))
         (reply (cadr autoreply)))
-    (let ((target-server (car matcher)))
-      (when (and (or (not target-server)
-                     (and (equal server target-server)))
+    (let ((target-network (car matcher)))
+      (when (and (or (not target-network)
+                     (and (equal network target-network)))
                  (murk--lists-equal (cdr matcher)
                  (murk--lists-equal (cdr matcher)
-                                    (append (list (lurk-msg-src msg)
-                                                  (lurk-msg-cmd msg))
+                                    (append (list (murk-msg-src msg)
+                                                  (murk-msg-cmd msg))
                                             (murk-msg-params msg))))
                                             (murk-msg-params msg))))
-        (murk-send-msg server
+        (murk-send-msg network
          (murk-msg nil nil (car reply) (cdr reply)))))))
 
          (murk-msg nil nil (car reply) (cdr reply)))))))
 
-(defun murk-process-autoreplies (server msg)
+(defun murk-process-autoreplies (network msg)
   (mapc
    (lambda (autoreply)
   (mapc
    (lambda (autoreply)
-     (murk-process-autoreply server msg autoreply))
+     (murk-process-autoreply network msg autoreply))
    murk-autoreply-table))
 
 
    murk-autoreply-table))
 
 
@@ -963,11 +1012,12 @@ The head of this list is always the current context.")
     ("CONNECT" "Connect to an IRC network." murk-command-connect murk-network-completions)
     ("QUIT" "Disconnect from current network." murk-command-quit)
     ("JOIN" "Join one or more channels." murk-command-join)
     ("CONNECT" "Connect to an IRC network." murk-command-connect murk-network-completions)
     ("QUIT" "Disconnect from current network." murk-command-quit)
     ("JOIN" "Join one or more channels." murk-command-join)
-    ("PART" "Leave channel." murk-command-part murk-context-completions)
+    ("PART" "Leave channel." murk-command-part murk-channel-completions)
+    ("SWITCHCONTEXT" "Switch current context" murk-command-switch-context murk-context-completions)
     ("NICK" "Change nick." murk-command-nick)
     ("LIST" "Display details of one or more channels." murk-command-list)
     ("TOPIC" "Set/query topic for current channel." murk-command-topic)
     ("NICK" "Change nick." murk-command-nick)
     ("LIST" "Display details of one or more channels." murk-command-list)
     ("TOPIC" "Set/query topic for current channel." murk-command-topic)
-    ("USERS" "List nicks of users in current context." murk-command-users)
+    ("USERS" "List nicks of users in current channel." murk-command-users)
     ("MSG" "Send private message to user." murk-command-msg murk-nick-completions)
     ("ME" "Display action." murk-command-me)
     ("VERSION" "Request version of another user's client via CTCP." murk-command-version murk-nick-completions)
     ("MSG" "Send private message to user." murk-command-msg murk-nick-completions)
     ("ME" "Display action." murk-command-me)
     ("VERSION" "Request version of another user's client via CTCP." murk-command-version murk-nick-completions)
@@ -981,6 +1031,19 @@ The head of this list is always the current context.")
 (defun murk-network-completions ()
   (mapcar (lambda (row) (car row)) murk-networks))
 
 (defun murk-network-completions ()
   (mapcar (lambda (row) (car row)) murk-networks))
 
+(defun murk-help-completions ()
+  (mapcar (lambda (row) (car row)) murk-command-table))
+
+(defun murk-channel-completions ()
+  (mapcar (lambda (ctx)
+            (murk-context->string ctx))
+          (seq-filter (lambda (ctx)
+                        (not (murk-network-context-p ctx)))
+                      murk-contexts)))
+
+(defun murk-context-completions ()
+  (mapcar (lambda (ctx) (murk-context->string ctx)) murk-contexts))
+
 (defun murk-command-help (params)
   (if params
       (let* ((cmd-str (upcase (car params)))
 (defun murk-command-help (params)
   (if params
       (let* ((cmd-str (upcase (car params)))
@@ -1035,43 +1098,55 @@ The head of this list is always the current context.")
 (defun murk-command-networks (_params)
   (murk-display-notice nil "Currently-known networks:")
   (dolist (row murk-networks)
 (defun murk-command-networks (_params)
   (murk-display-notice nil "Currently-known networks:")
   (dolist (row murk-networks)
-    (seq-let (network server port &rest _others) row
+    (seq-let (network network port &rest _others) row
       (murk-display-notice nil "\t" network
       (murk-display-notice nil "\t" network
-                           " [" server
+                           " [" network
                            " " (number-to-string port) "]")))
   (murk-display-notice nil "(Modify the `murk-networks' variable to add more.)"))
 
 (defun murk-command-quit (params)
   (let ((ctx (murk-current-context)))
     (if (not ctx)
                            " " (number-to-string port) "]")))
   (murk-display-notice nil "(Modify the `murk-networks' variable to add more.)"))
 
 (defun murk-command-quit (params)
   (let ((ctx (murk-current-context)))
     (if (not ctx)
-        (murk-display-error "No current server")
+        (murk-display-error "No current network")
       (let ((quit-msg (if params (string-join params " ") murk-default-quit-msg)))
         (murk-send-msg
       (let ((quit-msg (if params (string-join params " ") murk-default-quit-msg)))
         (murk-send-msg
-         (murk-context-server ctx)
+         (murk-context-network ctx)
          (murk-msg nil nil "QUIT" quit-msg))))))
 
 (defun murk-command-join (params)
   (if params
          (murk-msg nil nil "QUIT" quit-msg))))))
 
 (defun murk-command-join (params)
   (if params
-      (let ((server (murk-context-server (murk-current-context))))
+      (let ((network (murk-context-network (murk-current-context))))
         (dolist (channel params)
         (dolist (channel params)
-          (murk-send-msg server (murk-msg nil nil "JOIN" channel))))
+          (murk-send-msg network (murk-msg nil nil "JOIN" channel))))
     (murk-display-notice nil "Usage: /join channel [channel2 ...]")))
 
 (defun murk-command-part (params)
     (murk-display-notice nil "Usage: /join channel [channel2 ...]")))
 
 (defun murk-command-part (params)
-  (let* ((server (murk-context-server (murk-current-context)))
-         (channel (if params
-                      (car params)
-                    (murk-context-channel (murk-current-context)))))
-    (if channel
-        (murk-send-msg server (murk-msg nil nil "PART" channel))
-      (murk-display-error "No current channel to leave"))))
+  (let ((ctx (cond
+              ((not params) (murk-current-context))
+              ((seq-contains (car params) "@") (murk-string->context (car params)))
+              (t (list (murk-context-network (murk-current-context))  (car params))))))
+    (let ((network (murk-context-network ctx))
+          (channel (murk-context-channel ctx)))
+      (if channel
+          (murk-send-msg network (murk-msg nil nil "PART" channel))
+        (murk-display-error "Specify which channel to leave")))))
+
+(defun murk-command-switch-context (params)
+  (if (not params)
+      (murk-display-notice nil "Usage: /switchcontext #channel@network")
+    (let ((ctx (murk-string->context (car params))))
+      (murk-switch-to-context ctx)
+      (murk-highlight-current-context)
+      (murk-render-prompt)
+      (if murk-zoomed
+          (murk-zoom-in (murk-current-context))))))
 
 (defun murk-command-nick (params)
   (if params
       (let ((new-nick (string-join params " "))
             (ctx (murk-current-context)))
         (if ctx
 
 (defun murk-command-nick (params)
   (if params
       (let ((new-nick (string-join params " "))
             (ctx (murk-current-context)))
         (if ctx
-            (murk-send-msg (murk-context-server ctx)
+            (murk-send-msg (murk-context-network ctx)
                            (murk-msg nil nil "NICK" new-nick))
           (murk-display-error "No current connection")))
     (murk-display-notice nil "Usage: /nick <new-nick>")))
                            (murk-msg nil nil "NICK" new-nick))
           (murk-display-error "No current connection")))
     (murk-display-notice nil "Usage: /nick <new-nick>")))
@@ -1080,51 +1155,53 @@ The head of this list is always the current context.")
   (let ((ctx (murk-current-context)))
     (if ctx
         (if (not params)
   (let ((ctx (murk-current-context)))
     (if ctx
         (if (not params)
-            (murk-display-notice nil "This command can generate lots of output. Use `/LIST -yes' if you really want this, or `/LIST <channel_regexp>' to reduce the output.")
-          (let ((server (murk-context-server ctx)))
+            (murk-display-notice nil "This command can generate lots of output."
+                                 " Use `/LIST -yes' if you really want this,"
+                                 " or `/LIST <channel_regexp>' to reduce the output.")
+          (let ((network (murk-context-network ctx)))
             (if (equal (upcase (car params)) "-YES")
             (if (equal (upcase (car params)) "-YES")
-                (murk-send-msg server (murk-msg nil nil "LIST"))
-              (murk-send-msg server (murk-msg nil nil "LIST"
+                (murk-send-msg network (murk-msg nil nil "LIST"))
+              (murk-send-msg network (murk-msg nil nil "LIST"
                                               (car params))))))
       (murk-display-error "No current connection"))))
 
 (defun murk-command-topic (params)
   (let ((ctx (murk-current-context)))
                                               (car params))))))
       (murk-display-error "No current connection"))))
 
 (defun murk-command-topic (params)
   (let ((ctx (murk-current-context)))
-    (if (and ctx (not (murk-server-context-p ctx)))
-        (let ((server (murk-context-server ctx))
+    (if (and ctx (not (murk-network-context-p ctx)))
+        (let ((network (murk-context-network ctx))
               (channel (murk-context-channel ctx)))
           (if params
               (channel (murk-context-channel ctx)))
           (if params
-              (murk-send-msg server
+              (murk-send-msg network
                              (murk-msg nil nil "TOPIC" channel
                                        (string-join params " ")))
                              (murk-msg nil nil "TOPIC" channel
                                        (string-join params " ")))
-            (murk-send-msg server
+            (murk-send-msg network
                            (murk-msg nil nil "TOPIC" channel))))
       (murk-display-notice nil "No current channel."))))
 
 (defun murk-command-msg (params)
                            (murk-msg nil nil "TOPIC" channel))))
       (murk-display-notice nil "No current channel."))))
 
 (defun murk-command-msg (params)
-  (let ((server (murk-context-server (murk-current-context))))
+  (let ((network (murk-context-network (murk-current-context))))
     (if (and params (>= (length params) 2))
         (let ((to (car params))
               (text (string-join (cdr params) " ")))
     (if (and params (>= (length params) 2))
         (let ((to (car params))
               (text (string-join (cdr params) " ")))
-          (murk-send-msg server (murk-msg nil nil "PRIVMSG" to text))
-          (murk-display-message server
-                                (murk-connection-nick server)
+          (murk-send-msg network (murk-msg nil nil "PRIVMSG" to text))
+          (murk-display-message network
+                                (murk-connection-nick network)
                                 to text))
       (murk-display-notice nil "Usage: /msg <nick> <message>"))))
 
 (defun murk-command-me (params)
   (let* ((ctx (murk-current-context))
                                 to text))
       (murk-display-notice nil "Usage: /msg <nick> <message>"))))
 
 (defun murk-command-me (params)
   (let* ((ctx (murk-current-context))
-         (server (murk-context-server ctx)))
-    (if (and ctx (not (murk-server-context-p ctx)))
+         (network (murk-context-network ctx)))
+    (if (and ctx (not (murk-network-context-p ctx)))
         (if params
             (let* ((channel (murk-context-channel ctx))
         (if params
             (let* ((channel (murk-context-channel ctx))
-                   (my-nick (murk-connection-nick server))
+                   (my-nick (murk-connection-nick network))
                    (action (string-join params " "))
                    (ctcp-text (concat "\01ACTION " action "\01")))
                    (action (string-join params " "))
                    (ctcp-text (concat "\01ACTION " action "\01")))
-              (murk-send-msg server
+              (murk-send-msg network
                              (murk-msg nil nil "PRIVMSG"
                                        (list channel ctcp-text)))
                              (murk-msg nil nil "PRIVMSG"
                                        (list channel ctcp-text)))
-              (murk-display-action server my-nick channel action))
+              (murk-display-action network my-nick channel action))
           (murk-display-notice nil "Usage: /me <action>"))
       (murk-display-notice nil "No current channel."))))
 
           (murk-display-notice nil "Usage: /me <action>"))
       (murk-display-notice nil "No current channel."))))
 
@@ -1132,23 +1209,23 @@ The head of this list is always the current context.")
   (let ((ctx (murk-current-context)))
     (if ctx
         (if params
   (let ((ctx (murk-current-context)))
     (if ctx
         (if params
-            (let ((server (murk-context-server ctx))
+            (let ((network (murk-context-network ctx))
                   (nick (car params)))
                   (nick (car params)))
-              (murk-send-msg server
-                             (lurk-msg nil nil "PRIVMSG"
+              (murk-send-msg network
+                             (murk-msg nil nil "PRIVMSG"
                                        (list nick "\01VERSION\01")))
               (murk-display-notice ctx "CTCP version request sent to "
                                        (list nick "\01VERSION\01")))
               (murk-display-notice ctx "CTCP version request sent to "
-                                   nick " on " server))
+                                   nick " on " network))
           (murk-display-notice ctx "Usage: /version <nick>"))
       (murk-display-notice nil "No current channel."))))
 
 (defun murk-command-users (_params)
   (let ((ctx (murk-current-context)))
           (murk-display-notice ctx "Usage: /version <nick>"))
       (murk-display-notice nil "No current channel."))))
 
 (defun murk-command-users (_params)
   (let ((ctx (murk-current-context)))
-    (if (and ctx (not (murk-server-context-p ctx)))
+    (if (and ctx (not (murk-network-context-p ctx)))
         (let ((channel (murk-context-channel ctx))
         (let ((channel (murk-context-channel ctx))
-              (server (murk-context-server ctx))
-              (users (murk-context-users ctx)))
-          (murk-display-notice ctx "Users in " channel " on " server ":")
+              (network (murk-context-network ctx))
+              (users (murk-get-context-users ctx)))
+          (murk-display-notice ctx "Users in " channel " on " network ":")
           (murk-display-notice ctx (string-join users " ")))
       (murk-display-notice nil "No current channel."))))
 
           (murk-display-notice ctx (string-join users " ")))
       (murk-display-notice nil "No current channel."))))
 
@@ -1170,20 +1247,20 @@ The head of this list is always the current context.")
            (if (and command-row (elt command-row 2))
                (funcall (elt command-row 2) params)
              (murk-send-msg
            (if (and command-row (elt command-row 2))
                (funcall (elt command-row 2) params)
              (murk-send-msg
-              (murk-context-server (murk-current-context))
+              (murk-context-network (murk-current-context))
               (murk-msg nil nil (upcase cmd-str) params)))))
         (_
          (murk-display-error "Badly formed command")))
     (unless (string-empty-p string)
       (let ((ctx (murk-current-context)))
         (if ctx
               (murk-msg nil nil (upcase cmd-str) params)))))
         (_
          (murk-display-error "Badly formed command")))
     (unless (string-empty-p string)
       (let ((ctx (murk-current-context)))
         (if ctx
-            (if (not (murk-server-context-p ctx))
-                (let ((server (murk-context-server ctx))
+            (if (not (murk-network-context-p ctx))
+                (let ((network (murk-context-network ctx))
                       (channel (murk-context-channel ctx)))
                       (channel (murk-context-channel ctx)))
-                  (murk-send-msg server
+                  (murk-send-msg network
                                  (murk-msg nil nil "PRIVMSG" channel string))
                                  (murk-msg nil nil "PRIVMSG" channel string))
-                  (murk-display-message server
-                                        (murk-connection-nick server)
+                  (murk-display-message network
+                                        (murk-connection-nick network)
                                         channel string))
               (murk-display-error "No current channel"))
           (murk-display-error "No current context"))))))
                                         channel string))
               (murk-display-error "No current channel"))
           (murk-display-error "No current context"))))))
@@ -1283,7 +1360,7 @@ The head of this list is always the current context.")
                              (re-search-backward " " murk-input-marker t)))
                 (start (if space-idx (+ 1 space-idx) murk-input-marker)))
            (unless (string-prefix-p "/" (buffer-substring start end))
                              (re-search-backward " " murk-input-marker t)))
                 (start (if space-idx (+ 1 space-idx) murk-input-marker)))
            (unless (string-prefix-p "/" (buffer-substring start end))
-             (let* ((users (murk-context-users (murk-current-context)))
+             (let* ((users (murk-get-context-users (murk-current-context)))
                     (users-no@ (mapcar
                                 (lambda (u) (car (split-string u "@" t)))
                                 users)))
                     (users-no@ (mapcar
                                 (lambda (u) (car (split-string u "@" t)))
                                 users)))
@@ -1316,6 +1393,7 @@ The head of this list is always the current context.")
 ;;; Main start procedure
 ;;
 
 ;;; Main start procedure
 ;;
 
+;;;###autoload
 (defun murk ()
   "Start murk or just switch to the murk buffer if one already exists."
   (interactive)
 (defun murk ()
   "Start murk or just switch to the murk buffer if one already exists."
   (interactive)