;;; Contexts
;;
-;; A context is a list (network channel users) identifying the network
+;; 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.
;;
(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-network-context-p (ctx)
(not (cdr ctx)))
murk-contexts)))
(defun murk-context->string (ctx)
+ (concat
(if (murk-network-context-p ctx)
- (concat "[" (murk-context-network ctx) "]")
- (concat (murk-context-channel ctx) "@"
- (murk-context-network ctx))))
+ ""
+ (concat (murk-context-channel ctx) "@"))
+ (murk-context-network ctx)))
(defun murk-string->context (string)
(if (not (string-prefix-p "#" string))
(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))))
+;;; 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
- (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
- (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-network-user (network user)
(dolist (ctx murk-contexts)
(not (murk-network-context-p ctx)))
(murk-del-context-user ctx user))))
+(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)
(when (and (equal (murk-context-network ctx) network)
- (member old-nick (murk-context-users ctx)))
+ (member old-nick (murk-get-context-users ctx)))
(murk-del-context-user ctx old-nick)
(murk-add-context-users ctx (list new-nick)))))
+
;;; Buffer
;;
(murk-context-channel ctx)
" ("
(number-to-string
- (length (murk-context-users ctx)))
+ (length (murk-get-context-users ctx)))
")"))))
"No connection")))))))
"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))
- (puthash short-ctx facelist murk-context-facelists))
+ (puthash context facelist murk-context-facelists))
facelist))
(defun murk--fill-strings (col indent &rest strings)
(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))
- (context-face (murk-get-context-facelist short-ctx)))
+ (context-face (murk-get-context-facelist context)))
(insert-before-markers
(murk--fill-strings
80
(propertize padded-timestamp
'face 'murk-timestamp
'read-only t
- 'context short-ctx
+ 'context context
'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
- 'context short-ctx
+ 'context context
'invisible context-atom)))))))
(murk-scroll-windows-to-last-line))
+(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)
(murk-get-context network to)
(propertize
(if (murk-network-context-p context)
(concat "[" from "->" to "]")
- (concat (murk-context->string context) " <" from ">"))
+ (concat
+ (murk-make-context-button context)
+ " <" from ">"))
'face (murk-get-context-facelist context))
text)))
(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)
(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)
(nick (elt params 0))
(text (string-join (seq-drop params 1) " ")))
(murk-set-connection-nick network nick)
- (murk-display-notice (murk-get-context network) text)))
+ (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))
(ctx (murk-get-context network channel)))
(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
(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)
- (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))
((and "JOIN"
(guard (equal (murk-connection-nick network)
(murk-msg-src msg))))
- (let ((channel (car (murk-msg-params msg))))
- (murk-add-context (list network 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)
"Joining channel " channel " on " network)
(murk-highlight-current-context)
((and "PART"
(guard (equal (murk-connection-nick network)
(murk-msg-src msg))))
- (let ((channel (car (murk-msg-params msg))))
- (murk-display-notice (murk-current-context) "Left channel " channel)
- (murk-remove-context (list network 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)))
("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
+ (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)))
("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)
- ("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)
(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)))
(murk-display-notice nil "Usage: /join channel [channel2 ...]")))
(defun murk-command-part (params)
- (let* ((network (murk-context-network (murk-current-context)))
- (channel (if params
- (car params)
- (murk-context-channel (murk-current-context)))))
- (if channel
- (murk-send-msg network (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 ((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.")
+ (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")
(murk-send-msg network (murk-msg nil nil "LIST"))
(if (and ctx (not (murk-network-context-p ctx)))
(let ((channel (murk-context-channel ctx))
(network (murk-context-network ctx))
- (users (murk-context-users 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."))))
(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)))