Added context switching primitives.
[lurk.git] / murk.el
diff --git a/murk.el b/murk.el
index 2e89982..64bc81d 100644 (file)
--- a/murk.el
+++ b/murk.el
   :type '(string))
 
 (defcustom murk-networks
-  '(("debug" "localhost" 6667 :notls)
+  '(("debug" "localhost" 6697)
     ("libera" "irc.libera.chat" 6697)
     ("tilde" "tilde.chat" 6697)
-    ("freenode" "chat.freenode.net" 6697))
+    ("freenode" "chat.freenode.net" 6697)
+    ("mbr" "mbrserver.com" 6667 :notls))
   "IRC networks."
   :type '(alist :key-type string))
 
@@ -312,12 +313,20 @@ The head of this list is always the current context.")
          (equal (seq-take c1 2)
                 (seq-take c2 2)))))
 
-(defun murk-context-server (ctx) (elt ctx 0))
-(defun murk-context-channel (ctx) (elt ctx 1))
-(defun murk-context-users (ctx) (elt ctx 2))
+(defun murk-context-server (ctx)
+  (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) (not (cdr ctx)))
+
+(defun murk-server-context-p (ctx)
+  (not (cdr ctx)))
 
 (defun murk-add-context (ctx)
   (add-to-list 'murk-contexts ctx))
@@ -340,13 +349,21 @@ The head of this list is always the current context.")
      (concat (murk-context-channel ctx) "@"
              (murk-context-server ctx))))
 
-(defun murk-get-context (server &optional name)
-  (if name
-      (let ((test-ctx (list server name)))
+(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))))
+
+(defun murk-get-context (server &optional channel)
+  (if channel
+      (let ((test-ctx (list server channel)))
         (seq-find (lambda (ctx)
                     (equal (seq-take ctx 2) test-ctx))
                   murk-contexts))
-    (assoc server murk-contexts)))
+    (car (member (list server) murk-contexts))))
 
 (defun murk-cycle-contexts (&optional reverse)
   (setq murk-contexts
@@ -357,6 +374,13 @@ The head of this list is always the current context.")
                (seq-take murk-contexts nminus1)))
           (append (cdr murk-contexts) (list (car murk-contexts))))))
 
+(defun murk-switch-to-context (ctx)
+  (setq murk-contexts
+        (let* ((new-head (memq ctx murk-contexts))
+               (new-tail (take (- (length murk-contexts)
+                                  (length new-head)))))
+          (append new-head new-tail))))
+
 (defun murk-add-context-users (ctx users)
   (murk-set-context-users
    ctx
@@ -367,6 +391,18 @@ The head of this list is always the current context.")
    ctx
    (delete user (murk-context-users ctx))))
 
+(defun murk-del-server-user (server user)
+  (dolist (ctx murk-contexts)
+    (if (equal (murk-context-server ctx) server)
+        (murk-del-context-user ctx user))))
+
+(defun murk-rename-server-user (server old-nick new-nick)
+  (dolist (ctx murk-contexts)
+    (when (and (equal (murk-context-server ctx) server)
+               (member old-nick (murk-context-users ctx)))
+      (murk-del-context-user ctx old-nick)
+      (murk-add-context-users ctx (list new-nick)))))
+
 ;;; Buffer
 ;;
 
@@ -504,6 +540,15 @@ The head of this list is always the current context.")
        (concat (murk-context->string context) " <" from ">"))
      text)))
 
+(defun murk-display-action (server from to action-text)
+  (let ((context (if (string-prefix-p "#" to)
+                     (murk-get-context server to)
+                   (murk-get-context server))))
+    (murk-display-string
+     context
+     (concat (murk-context->string context) " *")
+     from " " action-text)))
+
 (defun murk-display-notice (context &rest notices)
   (murk-display-string
    context
@@ -612,7 +657,7 @@ The head of this list is always the current context.")
 
       ("PONG")
 
-      ("001"
+      ("001" ; RPL_WELCOME
        (let* ((params (murk-msg-params msg))
               (nick (elt params 0))
               (text (string-join (seq-drop params 1) " ")))
@@ -640,13 +685,13 @@ The head of this list is always the current context.")
               " users in " channel)
            (murk-display-notice nil "End of " channel " names list."))))
 
-      ("331"
+      ("331" ; RPL_NOTOPIC
        (let* ((params (murk-msg-params msg))
               (channel (elt params 1))
               (ctx (murk-get-context server channel)))
          (murk-display-notice ctx "No topic set.")))
 
-      ("332"
+      ("332" ; RPL_TOPIC
        (let* ((params (murk-msg-params msg))
               (channel (elt params 1))
               (topic (elt params 2))
@@ -683,18 +728,41 @@ The head of this list is always the current context.")
          (murk-render-prompt)))
 
       ("PART"
-       (let* ((channel (car (lurk-msg-params msg)))
-              (nick (lurk-msg-src msg))
+       (let* ((channel (car (murk-msg-params msg)))
+              (nick (murk-msg-src msg))
               (ctx (murk-get-context server channel)))
          (murk-del-context-user ctx nick)
          (if murk-show-joins
              (murk-display-notice ctx nick " left channel " channel
                                   " on " server))))
 
+      ((and "NICK"
+            (guard (equal (murk-connection-nick server)
+                          (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 nil "Nick set to " new-nick " on " server)))
+
+      ("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)))
+
+      ("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)
+                              nick " set the topic: " topic)))
+
       ("QUIT"
        (let ((nick (murk-msg-src msg))
              (reason (mapconcat 'identity (murk-msg-params msg) " ")))
-         (murk-del-user nick)
+         (murk-del-server-user server nick)
          (if murk-show-joins
              (murk-display-notice nil nick " quit: " reason))))
 
@@ -727,7 +795,7 @@ The head of this list is always the current context.")
                                  " on " server " (no response sent)"))
 
            ((rx (: "\01ACTION " (let action-text (* (not "\01"))) "\01"))
-            (murk-display-action from to action-text))
+            (murk-display-action server from to action-text))
 
            (_
             (murk-display-message server from to text)))))
@@ -748,7 +816,11 @@ The head of this list is always the current context.")
     ("JOIN" "Join one or more channels." murk-command-join)
     ("PART" "Leave channel." murk-command-part 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)
+    ("USERS" "List nicks of users in current context." murk-command-users)
     ("MSG" "Send private message to user." murk-command-msg murk-nick-completions)
+    ("ME" "Display action." murk-command-me)
     ("CLEAR" "Clear buffer text." murk-command-clear murk-context-completions)
     ("HELP" "Display help on client commands." murk-command-help murk-help-completions))
   "Table of commands explicitly supported by murk.")
@@ -803,12 +875,6 @@ The head of this list is always the current context.")
   (murk-display-notice nil "Joins/parts will now be "
                        (if murk-show-joins "shown" "hidden") "."))
 
-(defun murk-command-clear (params)
-  (if (not params)
-      (murk-clear-buffer)
-    (dolist (context params)
-      (murk-clear-context context))))
-
 (defun murk-command-connect (params)
   (if params
       (let ((network (car params)))
@@ -850,6 +916,41 @@ The head of this list is always the current context.")
         (murk-send-msg server (murk-msg nil nil "PART" channel))
       (murk-display-error "No current channel to leave"))))
 
+(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-msg nil nil "NICK" new-nick))
+          (murk-display-error "No current connection")))
+    (murk-display-notice nil "Usage: /nick <new-nick>")))
+
+(defun murk-command-list (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)))
+            (if (equal (upcase (car params)) "-YES")
+                (murk-send-msg server (murk-msg nil nil "LIST"))
+              (murk-send-msg server (murk-msg nil nil "LIST"
+                                              (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))
+              (channel (murk-context-channel ctx)))
+          (if params
+              (murk-send-msg server
+                             (murk-msg nil nil "TOPIC" channel
+                                       (string-join params " ")))
+            (murk-send-msg server
+                           (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))))
     (if (and params (>= (length params) 2))
@@ -861,6 +962,33 @@ The head of this list is always the 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)))
+        (if params
+            (let* ((channel (murk-context-channel ctx))
+                   (my-nick (murk-connection-nick server))
+                   (action (string-join params " "))
+                   (ctcp-text (concat "\01ACTION " action "\01")))
+              (murk-send-msg server
+                             (murk-msg nil nil "PRIVMSG"
+                                       (list channel ctcp-text)))
+              (murk-display-action server my-nick channel action))
+          (murk-display-notice nil "Usage: /me <action>"))
+      (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)))
+        (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 ":")
+          (murk-display-notice ctx (string-join users " ")))
+      (murk-display-notice nil "No current channel."))))
+
+
 ;;; Command entering
 ;;