summaryrefslogtreecommitdiff
path: root/lisp/net/rcirc.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/net/rcirc.el')
-rw-r--r--lisp/net/rcirc.el1019
1 files changed, 639 insertions, 380 deletions
diff --git a/lisp/net/rcirc.el b/lisp/net/rcirc.el
index 5bc775b8957..3bdcdd92f86 100644
--- a/lisp/net/rcirc.el
+++ b/lisp/net/rcirc.el
@@ -55,9 +55,49 @@
:link '(custom-manual "(rcirc)")
:group 'applications)
-(defcustom rcirc-default-server "irc.freenode.net"
- "The default server to connect to."
- :type 'string
+(defcustom rcirc-server-alist
+ '(("irc.freenode.net" :channels ("#rcirc")))
+ "An alist of IRC connections to establish when running `rcirc'.
+Each element looks like (SERVER-NAME PARAMETERS).
+
+SERVER-NAME is a string describing the server to connect
+to.
+
+The optional PARAMETERS come in pairs PARAMETER VALUE.
+
+The following parameters are recognized:
+
+`:nick'
+
+VALUE must be a string. If absent, `rcirc-default-nick' is used
+for this connection.
+
+`:port'
+
+VALUE must be a number or string. If absent,
+`rcirc-default-port' is used.
+
+`:user-name'
+
+VALUE must be a string. If absent, `rcirc-default-user-name' is
+used.
+
+`:full-name'
+
+VALUE must be a string. If absent, `rcirc-default-full-name' is
+used.
+
+`:channels'
+
+VALUE must be a list of strings describing which channels to join
+when connecting to this server. If absent, no channels will be
+connected to automatically."
+ :type '(alist :key-type string
+ :value-type (plist :options ((:nick string)
+ (:port integer)
+ (:user-name string)
+ (:full-name string)
+ (:channels (repeat string)))))
:group 'rcirc)
(defcustom rcirc-default-port 6667
@@ -75,19 +115,13 @@
:type 'string
:group 'rcirc)
-(defcustom rcirc-default-user-full-name (if (string= (user-full-name) "")
- rcirc-default-user-name
- (user-full-name))
+(defcustom rcirc-default-full-name (if (string= (user-full-name) "")
+ rcirc-default-user-name
+ (user-full-name))
"The full name sent to the server when connecting."
:type 'string
:group 'rcirc)
-(defcustom rcirc-startup-channels-alist '(("^irc.freenode.net$" "#rcirc"))
- "Alist of channels to join at startup.
-Each element looks like (SERVER-REGEXP . CHANNEL-LIST)."
- :type '(alist :key-type string :value-type (repeat string))
- :group 'rcirc)
-
(defcustom rcirc-fill-flag t
"*Non-nil means line-wrap messages printed in channel buffers."
:type 'boolean
@@ -95,11 +129,9 @@ Each element looks like (SERVER-REGEXP . CHANNEL-LIST)."
(defcustom rcirc-fill-column nil
"*Column beyond which automatic line-wrapping should happen.
-If nil, use value of `fill-column'.
-If `window-width', use the window's width as maximum.
-If `frame-width', use the frame's width as maximum."
+If nil, use value of `fill-column'. If 'frame-width, use the
+maximum frame width."
:type '(choice (const :tag "Value of `fill-column'")
- (const :tag "Full window width" window-width)
(const :tag "Full frame width" frame-width)
(integer :tag "Number of columns"))
:group 'rcirc)
@@ -120,6 +152,11 @@ underneath each nick."
"If non-nil, activity in this buffer is considered low priority.")
(make-variable-buffer-local 'rcirc-low-priority-flag)
+(defvar rcirc-omit-mode nil
+ "Non-nil if Rcirc-Omit mode is enabled.
+Use the command `rcirc-omit-mode' to change this variable.")
+(make-variable-buffer-local 'rcirc-omit-mode)
+
(defcustom rcirc-time-format "%H:%M "
"*Describes how timestamps are printed.
Used as the first arg to `format-time-string'."
@@ -145,7 +182,8 @@ number. If zero or nil, no truncating is done."
:group 'rcirc)
(defcustom rcirc-scroll-show-maximum-output t
- "*If non-nil, scroll buffer to keep the point at the bottom of the window."
+ "*If non-nil, scroll buffer to keep the point at the bottom of
+the window."
:type 'boolean
:group 'rcirc)
@@ -285,6 +323,9 @@ and the cdr part is used for encoding."
(defvar rcirc-nick-table nil)
+(defvar rcirc-recent-quit-alist nil
+ "Alist of nicks that have recently quit or parted the channel.")
+
(defvar rcirc-nick-syntax-table
(let ((table (make-syntax-table text-mode-syntax-table)))
(mapc (lambda (c) (modify-syntax-entry c "w" table))
@@ -319,36 +360,72 @@ and the cdr part is used for encoding."
(defconst rcirc-id-string (concat "rcirc on GNU Emacs " emacs-version))
(defvar rcirc-startup-channels nil)
+
;;;###autoload
(defun rcirc (arg)
- "Connect to IRC.
-If ARG is non-nil, prompt for a server to connect to."
+ "Connect to all servers in `rcirc-server-alist'.
+
+Do not connect to a server if it is already connected.
+
+If ARG is non-nil, instead prompt for connection parameters."
(interactive "P")
(if arg
- (let* ((server (read-string "IRC Server: " rcirc-default-server))
- (port (read-string "IRC Port: " (number-to-string rcirc-default-port)))
- (nick (read-string "IRC Nick: " rcirc-default-nick))
+ (let* ((server (completing-read "IRC Server: "
+ rcirc-server-alist
+ nil nil
+ (caar rcirc-server-alist)))
+ (server-plist (cdr (assoc-string server rcirc-server-alist)))
+ (port (read-string "IRC Port: "
+ (number-to-string
+ (or (plist-get server-plist 'port)
+ rcirc-default-port))))
+ (nick (read-string "IRC Nick: "
+ (or (plist-get server-plist 'nick)
+ rcirc-default-nick)))
(channels (split-string
(read-string "IRC Channels: "
- (mapconcat 'identity (rcirc-startup-channels server) " "))
+ (mapconcat 'identity
+ (plist-get server-plist
+ 'channels)
+ " "))
"[, ]+" t)))
- (rcirc-connect server port nick rcirc-default-user-name rcirc-default-user-full-name
+ (rcirc-connect server port nick rcirc-default-user-name
+ rcirc-default-full-name
channels))
- ;; make new connection using defaults unless already connected to
- ;; the default rcirc-server
- (let (connected)
- (dolist (p (rcirc-process-list))
- (when (string= rcirc-default-server (process-name p))
- (setq connected p)))
- (if (not connected)
- (rcirc-connect rcirc-default-server rcirc-default-port
- rcirc-default-nick rcirc-default-user-name
- rcirc-default-user-full-name
- (rcirc-startup-channels rcirc-default-server))
- (switch-to-buffer (process-buffer connected))
- (message "Connected to %s"
- (process-contact (get-buffer-process (current-buffer))
- :host))))))
+ ;; connect to servers in `rcirc-server-alist'
+ (let (connected-servers)
+ (dolist (c rcirc-server-alist)
+ (let ((server (car c))
+ (nick (or (plist-get (cdr c) :nick) rcirc-default-nick))
+ (port (or (plist-get (cdr c) :port) rcirc-default-port))
+ (user-name (or (plist-get (cdr c) :user-name)
+ rcirc-default-user-name))
+ (full-name (or (plist-get (cdr c) :full-name)
+ rcirc-default-full-name))
+ (channels (plist-get (cdr c) :channels)))
+ (when server
+ (let (connected)
+ (dolist (p (rcirc-process-list))
+ (when (string= server (process-name p))
+ (setq connected p)))
+ (if (not connected)
+ (condition-case e
+ (rcirc-connect server port nick user-name
+ full-name channels)
+ (quit (message "Quit connecting to %s" server)))
+ (with-current-buffer (process-buffer connected)
+ (setq connected-servers
+ (cons (process-contact (get-buffer-process
+ (current-buffer)) :host)
+ connected-servers))))))))
+ (when connected-servers
+ (message "Already connected to %s"
+ (if (cdr connected-servers)
+ (concat (mapconcat 'identity (butlast connected-servers) ", ")
+ ", and "
+ (car (last connected-servers)))
+ (car connected-servers)))))))
+
;;;###autoload
(defalias 'irc 'rcirc)
@@ -365,7 +442,8 @@ If ARG is non-nil, prompt for a server to connect to."
(defvar rcirc-process nil)
;;;###autoload
-(defun rcirc-connect (&optional server port nick user-name full-name startup-channels)
+(defun rcirc-connect (server &optional port nick user-name full-name
+ startup-channels)
(save-excursion
(message "Connecting to %s..." server)
(let* ((inhibit-eol-conversion)
@@ -374,10 +452,9 @@ If ARG is non-nil, prompt for a server to connect to."
(string-to-number port)
port)
rcirc-default-port))
- (server (or server rcirc-default-server))
(nick (or nick rcirc-default-nick))
(user-name (or user-name rcirc-default-user-name))
- (full-name (or full-name rcirc-default-user-full-name))
+ (full-name (or full-name rcirc-default-full-name))
(startup-channels startup-channels)
(process (make-network-process :name server :host server :service port-number)))
;; set up process
@@ -412,6 +489,8 @@ If ARG is non-nil, prompt for a server to connect to."
(make-local-variable 'rcirc-connecting)
(setq rcirc-connecting t)
+ (add-hook 'auto-save-hook 'rcirc-log-write)
+
;; identify
(rcirc-send-string process (concat "NICK " nick))
(rcirc-send-string process (concat "USER " user-name
@@ -446,12 +525,21 @@ last ping."
(mapc (lambda (process)
(with-rcirc-process-buffer process
(when (not rcirc-connecting)
- (rcirc-send-string process (concat "PING " (rcirc-server-name process))))))
+ (rcirc-send-string process
+ (format "PRIVMSG %s :\C-aKEEPALIVE %f\C-a"
+ rcirc-nick
+ (time-to-seconds
+ (current-time)))))))
(rcirc-process-list))
;; no processes, clean up timer
(cancel-timer rcirc-keepalive-timer)
(setq rcirc-keepalive-timer nil)))
+(defun rcirc-handler-ctcp-KEEPALIVE (process target sender message)
+ (with-rcirc-process-buffer process
+ (setq header-line-format (format "%f" (- (time-to-seconds (current-time))
+ (string-to-number message))))))
+
(defvar rcirc-debug-buffer " *rcirc debug*")
(defvar rcirc-debug-flag nil
"If non-nil, write information to `rcirc-debug-buffer'.")
@@ -461,14 +549,13 @@ Debug text is written to `rcirc-debug-buffer' if `rcirc-debug-flag'
is non-nil."
(when rcirc-debug-flag
(save-excursion
- (save-window-excursion
- (set-buffer (get-buffer-create rcirc-debug-buffer))
- (goto-char (point-max))
- (insert (concat
- "["
- (format-time-string "%Y-%m-%dT%T ") (process-name process)
- "] "
- text))))))
+ (set-buffer (get-buffer-create rcirc-debug-buffer))
+ (goto-char (point-max))
+ (insert (concat
+ "["
+ (format-time-string "%Y-%m-%dT%T ") (process-name process)
+ "] "
+ text)))))
(defvar rcirc-sentinel-hooks nil
"Hook functions called when the process sentinel is called.
@@ -486,12 +573,16 @@ Functions are called with PROCESS and SENTINEL arguments.")
(process-name process)
sentinel
(process-status process)) (not rcirc-target))
- ;; remove the prompt from buffers
- (let ((inhibit-read-only t))
- (delete-region rcirc-prompt-start-marker
- rcirc-prompt-end-marker))))
+ (rcirc-disconnect-buffer)))
(run-hook-with-args 'rcirc-sentinel-hooks process sentinel))))
+(defun rcirc-disconnect-buffer (&optional buffer)
+ (with-current-buffer (or buffer (current-buffer))
+ ;; set rcirc-target to nil for each channel so cleanup
+ ;; doesnt happen when we reconnect
+ (setq rcirc-target nil)
+ (setq mode-line-process ":disconnected")))
+
(defun rcirc-process-list ()
"Return a list of rcirc processes."
(let (ps)
@@ -530,7 +621,6 @@ Function is called with PROCESS, COMMAND, SENDER, ARGS and LINE.")
process))))))
(defun rcirc-delete-process (process)
- (message "delete process %S" process)
(delete-process process))
(defvar rcirc-trap-errors-flag t)
@@ -593,7 +683,8 @@ With no argument or nil as argument, use the current buffer."
(defun rcirc-server-name (process)
"Return PROCESS server name, given by the 001 response."
(with-rcirc-process-buffer process
- (or rcirc-server-name rcirc-default-server)))
+ (or rcirc-server-name
+ (warn "server name for process %S unknown" process))))
(defun rcirc-nick (process)
"Return PROCESS nick."
@@ -610,9 +701,10 @@ With no argument or nil as argument, use the current buffer."
(defvar rcirc-max-message-length 420
"Messages longer than this value will be split.")
-(defun rcirc-send-message (process target message &optional noticep)
+(defun rcirc-send-message (process target message &optional noticep silent)
"Send TARGET associated with PROCESS a privmsg with text MESSAGE.
-If NOTICEP is non-nil, send a notice instead of privmsg."
+If NOTICEP is non-nil, send a notice instead of privmsg.
+If SILENT is non-nil, do not print the message in any irc buffer."
;; max message length is 512 including CRLF
(let* ((response (if noticep "NOTICE" "PRIVMSG"))
(oversize (> (length message) rcirc-max-message-length))
@@ -625,8 +717,9 @@ If NOTICEP is non-nil, send a notice instead of privmsg."
(more (if oversize
(substring message rcirc-max-message-length))))
(rcirc-get-buffer-create process target)
- (rcirc-print process (rcirc-nick process) response target text)
(rcirc-send-string process (concat response " " target " :" text))
+ (unless silent
+ (rcirc-print process (rcirc-nick process) response target text))
(when more (rcirc-send-message process target more noticep))))
(defvar rcirc-input-ring nil)
@@ -676,7 +769,6 @@ If NOTICEP is non-nil, send a notice instead of privmsg."
rcirc-target))))))
(let ((completion (car rcirc-nick-completions)))
(when completion
- (rcirc-put-nick-channel (rcirc-buffer-process) completion rcirc-target)
(delete-region (+ rcirc-prompt-end-marker
rcirc-nick-completion-start-offset)
(point))
@@ -711,7 +803,8 @@ If NOTICEP is non-nil, send a notice instead of privmsg."
(define-key rcirc-mode-map (kbd "C-c C-d") 'rcirc-cmd-mode)
(define-key rcirc-mode-map (kbd "C-c C-m") 'rcirc-cmd-msg)
(define-key rcirc-mode-map (kbd "C-c C-r") 'rcirc-cmd-nick) ; rename
-(define-key rcirc-mode-map (kbd "C-c C-o") 'rcirc-cmd-oper)
+(define-key rcirc-mode-map (kbd "C-c C-o") 'rcirc-omit-mode)
+(define-key rcirc-mode-map (kbd "M-o") 'rcirc-omit-mode)
(define-key rcirc-mode-map (kbd "C-c C-p") 'rcirc-cmd-part)
(define-key rcirc-mode-map (kbd "C-c C-q") 'rcirc-cmd-query)
(define-key rcirc-mode-map (kbd "C-c C-t") 'rcirc-cmd-topic)
@@ -737,6 +830,14 @@ If NOTICEP is non-nil, send a notice instead of privmsg."
(defvar rcirc-last-post-time nil)
+(defvar rcirc-log-alist nil
+ "Alist of lines to log to disk when `rcirc-log-flag' is non-nil.
+Each element looks like (FILENAME . TEXT).")
+
+(defvar rcirc-current-line 0
+ "The current number of responses printed in this channel.
+This number is independent of the number of lines in the buffer.")
+
(defun rcirc-mode (process target)
"Major mode for IRC channel buffers.
@@ -745,6 +846,7 @@ If NOTICEP is non-nil, send a notice instead of privmsg."
(use-local-map rcirc-mode-map)
(setq mode-name "rcirc")
(setq major-mode 'rcirc-mode)
+ (setq mode-line-process nil)
(make-local-variable 'rcirc-input-ring)
(setq rcirc-input-ring (make-ring rcirc-input-ring-size))
@@ -756,12 +858,26 @@ If NOTICEP is non-nil, send a notice instead of privmsg."
(setq rcirc-topic nil)
(make-local-variable 'rcirc-last-post-time)
(setq rcirc-last-post-time (current-time))
+ (make-local-variable 'fill-paragraph-function)
+ (setq fill-paragraph-function 'rcirc-fill-paragraph)
+ (make-local-variable 'rcirc-recent-quit-alist)
+ (setq rcirc-recent-quit-alist nil)
+ (make-local-variable 'rcirc-current-line)
+ (setq rcirc-current-line 0)
(make-local-variable 'rcirc-short-buffer-name)
(setq rcirc-short-buffer-name nil)
(make-local-variable 'rcirc-urls)
(setq use-hard-newlines t)
+ ;; setup for omitting responses
+ (setq buffer-invisibility-spec '())
+ (setq buffer-display-table (make-display-table))
+ (set-display-table-slot buffer-display-table 4
+ (let ((glyph (make-glyph-code
+ ?. 'font-lock-keyword-face)))
+ (make-vector 3 glyph)))
+
(make-local-variable 'rcirc-decode-coding-system)
(make-local-variable 'rcirc-encode-coding-system)
(dolist (i rcirc-coding-system-alist)
@@ -873,14 +989,16 @@ If ALL is non-nil, update prompts in all IRC buffers."
(when rcirc-target
(rcirc-remove-nick-channel (rcirc-buffer-process)
(rcirc-buffer-nick)
- rcirc-target))))))
+ rcirc-target))))
+ (setq rcirc-target nil)))
(defun rcirc-generate-new-buffer-name (process target)
"Return a buffer name based on PROCESS and TARGET.
This is used for the initial name given to IRC buffers."
- (if target
- (concat target "@" (process-name process))
- (concat "*" (process-name process) "*")))
+ (substring-no-properties
+ (if target
+ (concat target "@" (process-name process))
+ (concat "*" (process-name process) "*"))))
(defun rcirc-get-buffer (process target &optional server)
"Return the buffer associated with the PROCESS and TARGET.
@@ -902,14 +1020,15 @@ Create the buffer if it doesn't exist."
(when (not rcirc-target)
(setq rcirc-target target))
buffer)
- ;; create the buffer
- (with-rcirc-process-buffer process
- (let ((new-buffer (get-buffer-create
- (rcirc-generate-new-buffer-name process target))))
- (with-current-buffer new-buffer
- (rcirc-mode process target))
- (rcirc-put-nick-channel process (rcirc-nick process) target)
- new-buffer)))))
+ ;; create the buffer
+ (with-rcirc-process-buffer process
+ (let ((new-buffer (get-buffer-create
+ (rcirc-generate-new-buffer-name process target))))
+ (with-current-buffer new-buffer
+ (rcirc-mode process target)
+ (rcirc-put-nick-channel process (rcirc-nick process) target
+ rcirc-current-line))
+ new-buffer)))))
(defun rcirc-send-input ()
"Send input to target associated with the current buffer."
@@ -943,6 +1062,14 @@ Create the buffer if it doesn't exist."
(ring-insert rcirc-input-ring input)
(setq rcirc-input-ring-index 0))))))
+(defun rcirc-fill-paragraph (&optional arg)
+ (interactive "p")
+ (when (> (point) rcirc-prompt-end-marker)
+ (save-restriction
+ (narrow-to-region rcirc-prompt-end-marker (point-max))
+ (let ((fill-column rcirc-max-message-length))
+ (fill-region (point-min) (point-max))))))
+
(defun rcirc-process-input-line (line)
(if (string-match "^/\\([^ ]+\\) ?\\(.*\\)$" line)
(rcirc-process-command (match-string 1 line)
@@ -984,7 +1111,8 @@ Create the buffer if it doesn't exist."
(interactive)
(let ((pos (1+ (- (point) rcirc-prompt-end-marker))))
(goto-char (point-max))
- (let ((text (buffer-substring rcirc-prompt-end-marker (point)))
+ (let ((text (buffer-substring-no-properties rcirc-prompt-end-marker
+ (point)))
(parent (buffer-name)))
(delete-region rcirc-prompt-end-marker (point))
(setq rcirc-window-configuration (current-window-configuration))
@@ -1021,7 +1149,6 @@ Create the buffer if it doesn't exist."
(defun rcirc-multiline-minor-submit ()
"Send the text in buffer back to parent buffer."
(interactive)
- (assert rcirc-parent-buffer)
(untabify (point-min) (point-max))
(let ((text (buffer-substring (point-min) (point-max)))
(buffer (current-buffer))
@@ -1052,12 +1179,12 @@ Create the buffer if it doesn't exist."
(process-buffer process)))))
(defcustom rcirc-response-formats
- '(("PRIVMSG" . "%T<%N> %m")
- ("NOTICE" . "%T-%N- %m")
- ("ACTION" . "%T[%N %m]")
- ("COMMAND" . "%T%m")
- ("ERROR" . "%T%fw!!! %m")
- (t . "%T%fp*** %fs%n %r %m"))
+ '(("PRIVMSG" . "<%N> %m")
+ ("NOTICE" . "-%N- %m")
+ ("ACTION" . "[%N %m]")
+ ("COMMAND" . "%m")
+ ("ERROR" . "%fw!!! %m")
+ (t . "%fp*** %fs%n %r %m"))
"An alist of formats used for printing responses.
The format is looked up using the response-type as a key;
if no match is found, the default entry (with a key of `t') is used.
@@ -1069,7 +1196,6 @@ the of the following escape sequences replaced by the described values:
%n The sender's nick
%N The sender's nick (with face `rcirc-my-nick' or `rcirc-other-nick')
%r The response-type
- %T The timestamp (with face `rcirc-timestamp')
%t The target
%fw Following text uses the face `font-lock-warning-face'
%fp Following text uses the face `rcirc-server-prefix'
@@ -1082,92 +1208,67 @@ the of the following escape sequences replaced by the described values:
:value-type string)
:group 'rcirc)
+(defcustom rcirc-omit-responses
+ '("JOIN" "PART" "QUIT" "NICK")
+ "Responses which will be hidden when `rcirc-omit-mode' is enabled."
+ :type '(repeat string)
+ :group 'rcirc)
+
(defun rcirc-format-response-string (process sender response target text)
"Return a nicely-formatted response string, incorporating TEXT
\(and perhaps other arguments). The specific formatting used
is found by looking up RESPONSE in `rcirc-response-formats'."
- (let ((chunks
- (split-string (or (cdr (assoc response rcirc-response-formats))
- (cdr (assq t rcirc-response-formats)))
- "%"))
- (sender (or sender ""))
- (result "")
- (face nil)
- key face-key repl)
- (when (equal (car chunks) "")
- (pop chunks))
- (dolist (chunk chunks)
- (if (equal chunk "")
- (setq key ?%)
- (setq key (aref chunk 0))
- (setq chunk (substring chunk 1)))
- (setq repl
- (cond ((eq key ?%)
- ;; %% -- literal % character
- "%")
- ((or (eq key ?n) (eq key ?N))
- ;; %n/%N -- nick
- (let ((nick (concat (if (string= (rcirc-server-name process)
- sender)
- ""
- sender)
- (and target (concat "," target)))))
- (rcirc-facify nick
- (if (eq key ?n)
- face
- (cond ((string= sender (rcirc-nick process))
- 'rcirc-my-nick)
- ((and rcirc-bright-nicks
- (string-match
- (regexp-opt rcirc-bright-nicks)
- sender))
- 'rcirc-bright-nick)
- ((and rcirc-dim-nicks
- (string-match
- (regexp-opt rcirc-dim-nicks)
- sender))
- 'rcirc-dim-nick)
- (t
- 'rcirc-other-nick))))))
- ((eq key ?T)
- ;; %T -- timestamp
- (rcirc-facify
- (format-time-string rcirc-time-format (current-time))
- 'rcirc-timestamp))
- ((eq key ?m)
- ;; %m -- message text
- (rcirc-markup-text process sender response (rcirc-facify text face)))
- ((eq key ?t)
- ;; %t -- target
- (rcirc-facify (or rcirc-target "") face))
- ((eq key ?r)
- ;; %r -- response
- (rcirc-facify response face))
- ((eq key ?f)
- ;; %f -- change face
- (setq face-key (aref chunk 0))
- (setq chunk (substring chunk 1))
- (cond ((eq face-key ?w)
- ;; %fw -- warning face
- (setq face 'font-lock-warning-face))
- ((eq face-key ?p)
- ;; %fp -- server-prefix face
- (setq face 'rcirc-server-prefix))
- ((eq face-key ?s)
- ;; %fs -- warning face
- (setq face 'rcirc-server))
- ((eq face-key ?-)
- ;; %fs -- warning face
- (setq face nil))
- ((and (eq face-key ?\[)
- (string-match "^\\([^]]*\\)[]]" chunk)
- (facep (match-string 1 chunk)))
- ;; %f[...] -- named face
- (setq face (intern (match-string 1 chunk)))
- (setq chunk (substring chunk (match-end 0)))))
- "")))
- (setq result (concat result repl (rcirc-facify chunk face))))
- result))
+ (with-temp-buffer
+ (insert (or (cdr (assoc response rcirc-response-formats))
+ (cdr (assq t rcirc-response-formats))))
+ (goto-char (point-min))
+ (let ((start (point-min))
+ (sender (if (or (not sender)
+ (string= (rcirc-server-name process) sender))
+ ""
+ sender))
+ face)
+ (while (re-search-forward "%\\(\\(f\\(.\\)\\)\\|\\(.\\)\\)" nil t)
+ (rcirc-add-face start (match-beginning 0) face)
+ (setq start (match-beginning 0))
+ (replace-match
+ (case (aref (match-string 1) 0)
+ (?f (setq face
+ (case (string-to-char (match-string 3))
+ (?w 'font-lock-warning-face)
+ (?p 'rcirc-server-prefix)
+ (?s 'rcirc-server)
+ (t nil)))
+ "")
+ (?n sender)
+ (?N (let ((my-nick (rcirc-nick process)))
+ (save-match-data
+ (with-syntax-table rcirc-nick-syntax-table
+ (rcirc-facify sender
+ (cond ((string= sender my-nick)
+ 'rcirc-my-nick)
+ ((and rcirc-bright-nicks
+ (string-match
+ (regexp-opt rcirc-bright-nicks
+ 'words)
+ sender))
+ 'rcirc-bright-nick)
+ ((and rcirc-dim-nicks
+ (string-match
+ (regexp-opt rcirc-dim-nicks
+ 'words)
+ sender))
+ 'rcirc-dim-nick)
+ (t
+ 'rcirc-other-nick)))))))
+ (?m (propertize text 'rcirc-text text))
+ (?r response)
+ (?t (or target ""))
+ (t (concat "UNKNOWN CODE:" (match-string 0))))
+ t t nil 0)
+ (rcirc-add-face (match-beginning 0) (match-end 0) face))
+ (rcirc-add-face start (match-beginning 0) face))
+ (buffer-substring (point-min) (point-max))))
(defun rcirc-target-buffer (process sender response target text)
"Return a buffer to print the server response."
@@ -1177,7 +1278,8 @@ is found by looking up RESPONSE in `rcirc-response-formats'."
(rcirc-any-buffer process))
((not (rcirc-channel-p target))
;; message from another user
- (if (string= response "PRIVMSG")
+ (if (or (string= response "PRIVMSG")
+ (string= response "ACTION"))
(rcirc-get-buffer-create process (if (string= sender rcirc-nick)
target
sender))
@@ -1190,16 +1292,74 @@ is found by looking up RESPONSE in `rcirc-response-formats'."
(defvar rcirc-last-sender nil)
(make-variable-buffer-local 'rcirc-last-sender)
+(defcustom rcirc-log-directory "~/.emacs.d/rcirc-log"
+ "Directory to keep IRC logfiles."
+ :type 'directory
+ :group 'rcirc)
+
+(defcustom rcirc-log-flag nil
+ "Non-nil means log IRC activity to disk.
+Logfiles are kept in `rcirc-log-directory'."
+ :type 'boolean
+ :group 'rcirc)
+
+(defcustom rcirc-omit-threshold 100
+ "Number of lines since last activity from a nick before `rcirc-omit-responses' are omitted."
+ :type 'integer
+ :group 'rcirc)
+
+(defun rcirc-last-quit-line (process nick target)
+ "Return the line number where NICK left TARGET.
+Returns nil if the information is not recorded."
+ (let ((chanbuf (rcirc-get-buffer process target)))
+ (when chanbuf
+ (cdr (assoc-string nick (with-current-buffer chanbuf
+ rcirc-recent-quit-alist))))))
+
+(defun rcirc-last-line (process nick target)
+ "Return the line from the last activity from NICK in TARGET."
+ (let* ((chanbuf (rcirc-get-buffer process target))
+ (line (or (cdr (assoc-string target
+ (gethash nick (with-rcirc-server-buffer
+ rcirc-nick-table)) t))
+ (rcirc-last-quit-line process nick target))))
+ (if line
+ line
+ ;;(message "line is nil for %s in %s" nick target)
+ nil)))
+
+(defun rcirc-elapsed-lines (process nick target)
+ "Return the number of lines since activity from NICK in TARGET."
+ (let ((last-activity-line (rcirc-last-line process nick target)))
+ (when (and last-activity-line
+ (> last-activity-line 0))
+ (- rcirc-current-line last-activity-line))))
+
+(defvar rcirc-markup-text-functions
+ '(rcirc-markup-attributes
+ rcirc-markup-my-nick
+ rcirc-markup-urls
+ rcirc-markup-keywords
+ rcirc-markup-bright-nicks)
+
+ "List of functions used to manipulate text before it is printed.
+
+Each function takes two arguments, SENDER, and RESPONSE. The
+buffer is narrowed with the text to be printed and the point is
+at the beginning of the `rcirc-text' propertized text.")
+
(defun rcirc-print (process sender response target text &optional activity)
"Print TEXT in the buffer associated with TARGET.
Format based on SENDER and RESPONSE. If ACTIVITY is non-nil,
record activity."
(or text (setq text ""))
- (unless (or (member sender rcirc-ignore-list)
- (member (with-syntax-table rcirc-nick-syntax-table
- (when (string-match "^\\([^/]\\w*\\)[:,]" text)
- (match-string 1 text)))
- rcirc-ignore-list))
+ (unless (and (or (member sender rcirc-ignore-list)
+ (member (with-syntax-table rcirc-nick-syntax-table
+ (when (string-match "^\\([^/]\\w*\\)[:,]" text)
+ (match-string 1 text)))
+ rcirc-ignore-list))
+ ;; do not ignore if we sent the message
+ (not (string= sender (rcirc-nick process))))
(let* ((buffer (rcirc-target-buffer process sender response target text))
(inhibit-read-only t))
(with-current-buffer buffer
@@ -1212,7 +1372,8 @@ record activity."
(setq text (decode-coding-string text rcirc-decode-coding-system))
;; mark the line with overlay arrow
(unless (or (marker-position overlay-arrow-position)
- (get-buffer-window (current-buffer)))
+ (get-buffer-window (current-buffer))
+ (member response rcirc-omit-responses))
(set-marker overlay-arrow-position
(marker-position rcirc-prompt-start-marker))))
@@ -1222,44 +1383,46 @@ record activity."
(set-marker-insertion-type rcirc-prompt-start-marker t)
(set-marker-insertion-type rcirc-prompt-end-marker t)
- (let ((fmted-text
- (rcirc-format-response-string process sender response nil
- text)))
-
- (insert fmted-text (propertize "\n" 'hard t))
- (set-marker-insertion-type rcirc-prompt-start-marker nil)
- (set-marker-insertion-type rcirc-prompt-end-marker nil)
-
- (let ((text-start (make-marker)))
- (set-marker text-start
- (or (next-single-property-change fill-start
- 'rcirc-text)
- rcirc-prompt-end-marker))
- ;; squeeze spaces out of text before rcirc-text
- (fill-region fill-start (1- text-start))
-
- ;; fill the text we just inserted, maybe
- (when (and rcirc-fill-flag
- (not (string= response "372"))) ;/motd
- (let ((fill-prefix
- (or rcirc-fill-prefix
- (make-string (- text-start fill-start) ?\s)))
- (fill-column (cond ((eq rcirc-fill-column 'frame-width)
- (1- (frame-width)))
- ((eq rcirc-fill-column 'window-width)
- (1- (window-width)))
- (rcirc-fill-column
- rcirc-fill-column)
- (t fill-column))))
- (fill-region fill-start rcirc-prompt-start-marker 'left t)))))
-
- ;; set inserted text to be read-only
- (when rcirc-read-only-flag
- (put-text-property rcirc-prompt-start-marker fill-start 'read-only t)
- (let ((inhibit-read-only t))
- (put-text-property rcirc-prompt-start-marker fill-start
- 'front-sticky t)
- (put-text-property (1- (point)) (point) 'rear-nonsticky t)))
+ (let ((start (point)))
+ (insert (rcirc-format-response-string process sender response nil
+ text)
+ (propertize "\n" 'hard t))
+
+ ;; squeeze spaces out of text before rcirc-text
+ (fill-region fill-start
+ (1- (or (next-single-property-change fill-start
+ 'rcirc-text)
+ rcirc-prompt-end-marker)))
+
+ ;; run markup functions
+ (save-excursion
+ (save-restriction
+ (narrow-to-region start rcirc-prompt-start-marker)
+ (goto-char (or (next-single-property-change start 'rcirc-text)
+ (point)))
+ (when (rcirc-buffer-process)
+ (save-excursion (rcirc-markup-timestamp sender response))
+ (dolist (fn rcirc-markup-text-functions)
+ (save-excursion (funcall fn sender response)))
+ (when rcirc-fill-flag
+ (save-excursion (rcirc-markup-fill sender response))))
+
+ (when rcirc-read-only-flag
+ (add-text-properties (point-min) (point-max)
+ '(read-only t front-sticky t))))
+ ;; make text omittable
+ (let ((last-activity-lines (rcirc-elapsed-lines process sender target)))
+ (if (and (not (string= (rcirc-nick process) sender))
+ (member response rcirc-omit-responses)
+ (or (not last-activity-lines)
+ (< rcirc-omit-threshold last-activity-lines)))
+ (put-text-property (1- start) (1- rcirc-prompt-start-marker)
+ 'invisible 'rcirc-omit)
+ ;; otherwise increment the line count
+ (setq rcirc-current-line (1+ rcirc-current-line))))))
+
+ (set-marker-insertion-type rcirc-prompt-start-marker nil)
+ (set-marker-insertion-type rcirc-prompt-end-marker nil)
;; truncate buffer if it is very long
(save-excursion
@@ -1275,27 +1438,26 @@ record activity."
(window-buffer w))
(>= (window-point w)
rcirc-prompt-end-marker))
- (set-window-point w (point-max))))
+ (set-window-point w (point-max))))
nil t)
;; restore the point
(goto-char (if moving rcirc-prompt-end-marker old-point))
- ;; keep window on bottom line if it was already there
+ ;; keep window on bottom line if it was already there
(when rcirc-scroll-show-maximum-output
(walk-windows (lambda (w)
(when (eq (window-buffer w) (current-buffer))
(with-current-buffer (window-buffer w)
(when (eq major-mode 'rcirc-mode)
(with-selected-window w
- (when (<= (- (window-height)
- (count-screen-lines
- (window-point)
- (window-start))
+ (when (<= (- (window-height)
+ (count-screen-lines (window-point)
+ (window-start))
1)
0)
(recenter -1)))))))
- nil t))
+ nil t))
;; flush undo (can we do something smarter here?)
(buffer-disable-undo)
@@ -1305,22 +1467,61 @@ record activity."
(when (and activity
(not rcirc-ignore-buffer-activity-flag)
(not (and rcirc-dim-nicks sender
- (string-match (regexp-opt rcirc-dim-nicks) sender))))
+ (string-match (regexp-opt rcirc-dim-nicks) sender)
+ (rcirc-channel-p target))))
(rcirc-record-activity (current-buffer)
(when (not (rcirc-channel-p rcirc-target))
'nick)))
+ (when rcirc-log-flag
+ (rcirc-log process sender response target text))
+
(sit-for 0) ; displayed text before hook
(run-hook-with-args 'rcirc-print-hooks
process sender response target text)))))
-(defun rcirc-startup-channels (server)
- "Return the list of startup channels for SERVER."
- (let (channels)
- (dolist (i rcirc-startup-channels-alist)
- (if (string-match (car i) server)
- (setq channels (append channels (cdr i)))))
- channels))
+(defcustom rcirc-log-filename-function 'rcirc-generate-new-buffer-name
+ "A function to generate the filename used by rcirc's logging facility.
+
+It is called with two arguments, PROCESS and TARGET (see
+`rcirc-generate-new-buffer-name' for their meaning), and should
+return the filename, or nil if no logging is desired for this
+session.
+
+If the returned filename is absolute (`file-name-absolute-p'
+returns true), then it is used as-is, otherwise the resulting
+file is put into `rcirc-log-directory'."
+ :group 'rcirc
+ :type 'function)
+
+(defun rcirc-log (process sender response target text)
+ "Record line in `rcirc-log', to be later written to disk."
+ (let ((filename (funcall rcirc-log-filename-function process target)))
+ (unless (null filename)
+ (let ((cell (assoc-string filename rcirc-log-alist))
+ (line (concat (format-time-string rcirc-time-format)
+ (substring-no-properties
+ (rcirc-format-response-string process sender
+ response target text))
+ "\n")))
+ (if cell
+ (setcdr cell (concat (cdr cell) line))
+ (setq rcirc-log-alist
+ (cons (cons filename line) rcirc-log-alist)))))))
+
+(defun rcirc-log-write ()
+ "Flush `rcirc-log-alist' data to disk.
+
+Log data is written to `rcirc-log-directory', except for
+log-files with absolute names (see `rcirc-log-filename-function')."
+ (dolist (cell rcirc-log-alist)
+ (let ((filename (expand-file-name (car cell) rcirc-log-directory))
+ (coding-system-for-write 'utf-8))
+ (make-directory (file-name-directory filename) t)
+ (with-temp-buffer
+ (insert (cdr cell))
+ (write-region (point-min) (point-max) filename t 'quiet))))
+ (setq rcirc-log-alist nil))
(defun rcirc-join-channels (process channels)
"Join CHANNELS."
@@ -1345,15 +1546,19 @@ record activity."
(mapcar (lambda (x) (car x))
(gethash nick rcirc-nick-table))))
-(defun rcirc-put-nick-channel (process nick channel)
- "Add CHANNEL to list associated with NICK."
+(defun rcirc-put-nick-channel (process nick channel &optional line)
+ "Add CHANNEL to list associated with NICK.
+Update the associated linestamp if LINE is non-nil.
+
+If the record doesn't exist, and LINE is nil, set the linestamp
+to zero."
(let ((nick (rcirc-user-nick nick)))
(with-rcirc-process-buffer process
(let* ((chans (gethash nick rcirc-nick-table))
(record (assoc-string channel chans t)))
(if record
- (setcdr record (current-time))
- (puthash nick (cons (cons channel (current-time))
+ (when line (setcdr record line))
+ (puthash nick (cons (cons channel (or line 0))
chans)
rcirc-nick-table))))))
@@ -1389,7 +1594,10 @@ record activity."
(setq nicks (cons (cons k (cdr record)) nicks)))))
rcirc-nick-table)
(mapcar (lambda (x) (car x))
- (sort nicks (lambda (x y) (time-less-p (cdr y) (cdr x)))))))
+ (sort nicks (lambda (x y)
+ (let ((lx (or (cdr x) 0))
+ (ly (or (cdr y) 0)))
+ (< ly lx)))))))
(list target))))
(defun rcirc-ignore-update-automatic (nick)
@@ -1437,6 +1645,9 @@ if NICK is also on `rcirc-ignore-list-automatic'."
(or (assq 'rcirc-low-priority-flag minor-mode-alist)
(setq minor-mode-alist
(cons '(rcirc-low-priority-flag " LowPri") minor-mode-alist)))
+(or (assq 'rcirc-omit-mode minor-mode-alist)
+ (setq minor-mode-alist
+ (cons '(rcirc-omit-mode " Omit") minor-mode-alist)))
(defun rcirc-toggle-ignore-buffer-activity ()
"Toggle the value of `rcirc-ignore-buffer-activity-flag'."
@@ -1458,30 +1669,48 @@ if NICK is also on `rcirc-ignore-list-automatic'."
"Activity in this buffer is normal priority"))
(force-mode-line-update))
-(defvar rcirc-switch-to-buffer-function 'switch-to-buffer
- "Function to use when switching buffers.
-Possible values are `switch-to-buffer', `pop-to-buffer', and
-`display-buffer'.")
+(defun rcirc-omit-mode ()
+ "Toggle the Rcirc-Omit mode.
+If enabled, \"uninteresting\" lines are not shown.
+Uninteresting lines are those whose responses are listed in
+`rcirc-omit-responses'."
+ (interactive)
+ (setq rcirc-omit-mode (not rcirc-omit-mode))
+ (if rcirc-omit-mode
+ (progn
+ (add-to-invisibility-spec '(rcirc-omit . t))
+ (message "Rcirc-Omit mode enabled"))
+ (remove-from-invisibility-spec '(rcirc-omit . t))
+ (message "Rcirc-Omit mode disabled"))
+ (recenter (when (> (point) rcirc-prompt-start-marker) -1)))
(defun rcirc-switch-to-server-buffer ()
"Switch to the server buffer associated with current channel buffer."
(interactive)
- (funcall rcirc-switch-to-buffer-function rcirc-server-buffer))
+ (switch-to-buffer rcirc-server-buffer))
(defun rcirc-jump-to-first-unread-line ()
"Move the point to the first unread line in this buffer."
(interactive)
- (when (marker-position overlay-arrow-position)
- (goto-char overlay-arrow-position)))
-
-(defvar rcirc-last-non-irc-buffer nil
- "The buffer to switch to when there is no more activity.")
+ (if (marker-position overlay-arrow-position)
+ (goto-char overlay-arrow-position)
+ (message "No unread messages")))
+
+(defun rcirc-non-irc-buffer ()
+ (let ((buflist (buffer-list))
+ buffer)
+ (while (and buflist (not buffer))
+ (with-current-buffer (car buflist)
+ (unless (or (eq major-mode 'rcirc-mode)
+ (= ?\s (aref (buffer-name) 0)) ; internal buffers
+ (get-buffer-window (current-buffer)))
+ (setq buffer (current-buffer))))
+ (setq buflist (cdr buflist)))
+ buffer))
(defun rcirc-next-active-buffer (arg)
- "Go to the next rcirc buffer with activity.
-With prefix ARG, go to the next low priority buffer with activity.
-The function given by `rcirc-switch-to-buffer-function' is used to
-show the buffer."
+ "Switch to the next rcirc buffer with activity.
+With prefix ARG, go to the next low priority buffer with activity."
(interactive "P")
(let* ((pair (rcirc-split-activity rcirc-activity))
(lopri (car pair))
@@ -1489,24 +1718,18 @@ show the buffer."
(if (or (and (not arg) hipri)
(and arg lopri))
(progn
- (unless (eq major-mode 'rcirc-mode)
- (setq rcirc-last-non-irc-buffer (current-buffer)))
- (funcall rcirc-switch-to-buffer-function
- (car (if arg lopri hipri))))
+ (switch-to-buffer (car (if arg lopri hipri)))
+ (when (> (point) rcirc-prompt-start-marker)
+ (recenter -1)))
(if (eq major-mode 'rcirc-mode)
- (if (not (and rcirc-last-non-irc-buffer
- (buffer-live-p rcirc-last-non-irc-buffer)))
- (message "No IRC activity. Start something.")
- (message "No more IRC activity. Go back to work.")
- (funcall rcirc-switch-to-buffer-function rcirc-last-non-irc-buffer)
- (setq rcirc-last-non-irc-buffer nil))
- (message (concat
- "No IRC activity."
- (when lopri
- (concat
- " Type C-u "
- (key-description (this-command-keys))
- " for low priority activity."))))))))
+ (switch-to-buffer (rcirc-non-irc-buffer))
+ (message "%s" (concat
+ "No IRC activity."
+ (when lopri
+ (concat
+ " Type C-u "
+ (key-description (this-command-keys))
+ " for low priority activity."))))))))
(defvar rcirc-activity-hooks nil
"Hook to be run when there is channel activity.
@@ -1518,23 +1741,33 @@ activity. Only run if the buffer is not visible and
(defun rcirc-record-activity (buffer &optional type)
"Record BUFFER activity with TYPE."
(with-current-buffer buffer
- (when (not (get-buffer-window (current-buffer) t))
- (setq rcirc-activity
- (sort (add-to-list 'rcirc-activity (current-buffer))
- (lambda (b1 b2)
- (let ((t1 (with-current-buffer b1 rcirc-last-post-time))
- (t2 (with-current-buffer b2 rcirc-last-post-time)))
- (time-less-p t2 t1)))))
- (pushnew type rcirc-activity-types)
- (rcirc-update-activity-string)))
+ (let ((old-activity rcirc-activity)
+ (old-types rcirc-activity-types))
+ (when (not (get-buffer-window (current-buffer) t))
+ (setq rcirc-activity
+ (sort (add-to-list 'rcirc-activity (current-buffer))
+ (lambda (b1 b2)
+ (let ((t1 (with-current-buffer b1 rcirc-last-post-time))
+ (t2 (with-current-buffer b2 rcirc-last-post-time)))
+ (time-less-p t2 t1)))))
+ (pushnew type rcirc-activity-types)
+ (unless (and (equal rcirc-activity old-activity)
+ (member type old-types))
+ (rcirc-update-activity-string)))))
(run-hook-with-args 'rcirc-activity-hooks buffer))
(defun rcirc-clear-activity (buffer)
"Clear the BUFFER activity."
- (setq rcirc-activity (delete buffer rcirc-activity))
+ (setq rcirc-activity (remove buffer rcirc-activity))
(with-current-buffer buffer
(setq rcirc-activity-types nil)))
+(defun rcirc-clear-unread (buffer)
+ "Erase the last read message arrow from BUFFER."
+ (when (buffer-live-p buffer)
+ (with-current-buffer buffer
+ (set-marker overlay-arrow-position nil))))
+
(defun rcirc-split-activity (activity)
"Return a cons cell with ACTIVITY split into (lopri . hipri)."
(let (lopri hipri)
@@ -1546,6 +1779,9 @@ activity. Only run if the buffer is not visible and
(add-to-list 'hipri buf t))))
(cons lopri hipri)))
+(defvar rcirc-update-activity-string-hook nil
+ "Hook run whenever the activity string is updated.")
+
;; TODO: add mouse properties
(defun rcirc-update-activity-string ()
"Update mode-line string."
@@ -1554,19 +1790,18 @@ activity. Only run if the buffer is not visible and
(hipri (cdr pair)))
(setq rcirc-activity-string
(cond ((or hipri lopri)
- (concat "-"
- (and hipri "[")
+ (concat (and hipri "[")
(rcirc-activity-string hipri)
(and hipri lopri ",")
(and lopri
(concat "("
(rcirc-activity-string lopri)
")"))
- (and hipri "]")
- "-"))
+ (and hipri "]")))
((not (null (rcirc-process-list)))
- "-[]-")
- (t "")))))
+ "[]")
+ (t "[]")))
+ (run-hooks 'rcirc-update-activity-string-hook)))
(defun rcirc-activity-string (buffers)
(mapconcat (lambda (b)
@@ -1586,33 +1821,47 @@ activity. Only run if the buffer is not visible and
(with-current-buffer buffer
(or rcirc-short-buffer-name (buffer-name))))
-(defvar rcirc-current-buffer nil)
-(defun rcirc-window-configuration-change ()
- "Go through visible windows and remove buffers from activity list.
-Also, clear the overlay arrow if the current buffer is now hidden."
- (let ((current-now-hidden t))
+(defun rcirc-visible-buffers ()
+ "Return a list of the visible buffers that are in rcirc-mode."
+ (let (acc)
(walk-windows (lambda (w)
- (let ((buf (window-buffer w)))
- (with-current-buffer buf
- (when (eq major-mode 'rcirc-mode)
- (rcirc-clear-activity buf)))
- (when (eq buf rcirc-current-buffer)
- (setq current-now-hidden nil)))))
- ;; add overlay arrow if the buffer isn't displayed
- (when (and current-now-hidden
- rcirc-current-buffer
- (buffer-live-p rcirc-current-buffer))
- (with-current-buffer rcirc-current-buffer
- (when (and (eq major-mode 'rcirc-mode)
- (marker-position overlay-arrow-position))
- (set-marker overlay-arrow-position nil)))))
-
- ;; remove any killed buffers from list
- (setq rcirc-activity
- (delq nil (mapcar (lambda (buf) (when (buffer-live-p buf) buf))
- rcirc-activity)))
- (rcirc-update-activity-string)
- (setq rcirc-current-buffer (current-buffer)))
+ (with-current-buffer (window-buffer w)
+ (when (eq major-mode 'rcirc-mode)
+ (push (current-buffer) acc)))))
+ acc))
+
+(defvar rcirc-visible-buffers nil)
+(defun rcirc-window-configuration-change ()
+ (unless (minibuffer-window-active-p (minibuffer-window))
+ ;; delay this until command has finished to make sure window is
+ ;; actually visible before clearing activity
+ (add-hook 'post-command-hook 'rcirc-window-configuration-change-1)))
+
+(defun rcirc-window-configuration-change-1 ()
+ ;; clear activity and overlay arrows
+ (let* ((old-activity rcirc-activity)
+ (hidden-buffers rcirc-visible-buffers))
+
+ (setq rcirc-visible-buffers (rcirc-visible-buffers))
+
+ (dolist (vbuf rcirc-visible-buffers)
+ (setq hidden-buffers (delq vbuf hidden-buffers))
+ ;; clear activity for all visible buffers
+ (rcirc-clear-activity vbuf))
+
+ ;; clear unread arrow from recently hidden buffers
+ (dolist (hbuf hidden-buffers)
+ (rcirc-clear-unread hbuf))
+
+ ;; remove any killed buffers from list
+ (setq rcirc-activity
+ (delq nil (mapcar (lambda (buf) (when (buffer-live-p buf) buf))
+ rcirc-activity)))
+ ;; update the mode-line string
+ (unless (equal old-activity rcirc-activity)
+ (rcirc-update-activity-string)))
+
+ (remove-hook 'post-command-hook 'rcirc-window-configuration-change-1))
;;; buffer name abbreviation
@@ -1722,8 +1971,9 @@ Also, clear the overlay arrow if the current buffer is now hidden."
(car (split-string channel)))))
(rcirc-send-string process (concat "JOIN " channel))
(when (not (eq (selected-window) (minibuffer-window)))
- (funcall rcirc-switch-to-buffer-function buffer))))
+ (switch-to-buffer buffer))))
+;; TODO: /part #channel reason, or consider removing #channel altogether
(defun-rcirc-command part (channel)
"Part CHANNEL."
(interactive "sPart channel: ")
@@ -1902,7 +2152,7 @@ keywords when no KEYWORD is given."
word-boundary))
(optional
(and "/"
- (1+ (char "-a-zA-Z0-9_=!?#$\@~`%&*+|\\/:;.,{}[]()"))
+ (1+ (char "-a-zA-Z0-9_='!?#$\@~`%&*+|\\/:;.,{}[]()"))
(char "-a-zA-Z0-9_=#$\@~`%&*+|\\/:;{}[]()")))))
"Regexp matching URLs. Set to nil to disable URL features in rcirc.")
@@ -1931,39 +2181,12 @@ keywords when no KEYWORD is given."
(rcirc-browse-url-at-point (posn-point position)))))
-(defvar rcirc-markup-text-functions
- '(rcirc-markup-body-text
- rcirc-markup-attributes
- rcirc-markup-my-nick
- rcirc-markup-urls
- rcirc-markup-keywords
- rcirc-markup-bright-nicks)
- "List of functions used to manipulate text before it is printed.
-
-Each function takes three arguments, PROCESS, SENDER, RESPONSE
-and CHANNEL-BUFFER. The current buffer is temporary buffer that
-contains the text to manipulate. Each function works on the text
-in this buffer.")
+(defun rcirc-markup-timestamp (sender response)
+ (goto-char (point-min))
+ (insert (rcirc-facify (format-time-string rcirc-time-format)
+ 'rcirc-timestamp)))
-(defun rcirc-markup-text (process sender response text)
- "Return TEXT with properties added based on various patterns."
- (let ((channel-buffer (current-buffer)))
- (with-temp-buffer
- (insert text)
- (goto-char (point-min))
- (dolist (fn rcirc-markup-text-functions)
- (save-excursion
- (funcall fn process sender response channel-buffer)))
- (buffer-substring (point-min) (point-max)))))
-
-(defun rcirc-markup-body-text (process sender response channel-buffer)
- ;; We add the text property `rcirc-text' to identify this as the
- ;; body text.
- (add-text-properties (point-min) (point-max)
- (list 'rcirc-text (buffer-substring-no-properties
- (point-min) (point-max)))))
-
-(defun rcirc-markup-attributes (process sender response channel-buffer)
+(defun rcirc-markup-attributes (sender response)
(while (re-search-forward "\\([\C-b\C-_\C-v]\\).*?\\(\\1\\|\C-o\\)" nil t)
(rcirc-add-face (match-beginning 0) (match-end 0)
(case (char-after (match-beginning 1))
@@ -1979,19 +2202,21 @@ in this buffer.")
(while (re-search-forward "\C-o+" nil t)
(delete-region (match-beginning 0) (match-end 0))))
-(defun rcirc-markup-my-nick (process sender response channel-buffer)
+(defun rcirc-markup-my-nick (sender response)
(with-syntax-table rcirc-nick-syntax-table
(while (re-search-forward (concat "\\b"
- (regexp-quote (rcirc-nick process))
+ (regexp-quote (rcirc-nick
+ (rcirc-buffer-process)))
"\\b")
nil t)
(rcirc-add-face (match-beginning 0) (match-end 0)
'rcirc-nick-in-message)
(when (string= response "PRIVMSG")
- (rcirc-add-face (point-min) (point-max) 'rcirc-nick-in-message-full-line)
- (rcirc-record-activity channel-buffer 'nick)))))
+ (rcirc-add-face (point-min) (point-max)
+ 'rcirc-nick-in-message-full-line)
+ (rcirc-record-activity (current-buffer) 'nick)))))
-(defun rcirc-markup-urls (process sender response channel-buffer)
+(defun rcirc-markup-urls (sender response)
(while (re-search-forward rcirc-url-regexp nil t)
(let ((start (match-beginning 0))
(end (match-end 0)))
@@ -1999,30 +2224,43 @@ in this buffer.")
(add-text-properties start end (list 'mouse-face 'highlight
'keymap rcirc-browse-url-map))
;; record the url
- (let ((url (buffer-substring-no-properties start end)))
- (with-current-buffer channel-buffer
- (push url rcirc-urls))))))
-
-(defun rcirc-markup-keywords (process sender response channel-buffer)
- (let* ((target (with-current-buffer channel-buffer (or rcirc-target "")))
- (keywords (delq nil (mapcar (lambda (keyword)
- (when (not (string-match keyword target))
- keyword))
- rcirc-keywords))))
- (when keywords
- (while (re-search-forward (regexp-opt keywords 'words) nil t)
- (rcirc-add-face (match-beginning 0) (match-end 0) 'rcirc-keyword)
- (when (and (string= response "PRIVMSG")
- (not (string= sender (rcirc-nick process))))
- (rcirc-record-activity channel-buffer 'keyword))))))
-
-(defun rcirc-markup-bright-nicks (process sender response channel-buffer)
+ (push (buffer-substring-no-properties start end) rcirc-urls))))
+
+(defun rcirc-markup-keywords (sender response)
+ (when (and (string= response "PRIVMSG")
+ (not (string= sender (rcirc-nick (rcirc-buffer-process)))))
+ (let* ((target (or rcirc-target ""))
+ (keywords (delq nil (mapcar (lambda (keyword)
+ (when (not (string-match keyword
+ target))
+ keyword))
+ rcirc-keywords))))
+ (when keywords
+ (while (re-search-forward (regexp-opt keywords 'words) nil t)
+ (rcirc-add-face (match-beginning 0) (match-end 0) 'rcirc-keyword)
+ (rcirc-record-activity (current-buffer) 'keyword))))))
+
+(defun rcirc-markup-bright-nicks (sender response)
(when (and rcirc-bright-nicks
(string= response "NAMES"))
(with-syntax-table rcirc-nick-syntax-table
(while (re-search-forward (regexp-opt rcirc-bright-nicks 'words) nil t)
(rcirc-add-face (match-beginning 0) (match-end 0)
'rcirc-bright-nick)))))
+
+(defun rcirc-markup-fill (sender response)
+ (when (not (string= response "372")) ; /motd
+ (let ((fill-prefix
+ (or rcirc-fill-prefix
+ (make-string (- (point) (line-beginning-position)) ?\s)))
+ (fill-column (- (cond ((eq rcirc-fill-column 'frame-width)
+ (1- (frame-width)))
+ (rcirc-fill-column
+ rcirc-fill-column)
+ (t fill-column))
+ ;; make sure ... doesn't cause line wrapping
+ 3)))
+ (fill-region (point) (point-max) nil t))))
;;; handlers
;; these are called with the server PROCESS, the SENDER, which is a
@@ -2031,7 +2269,6 @@ in this buffer.")
;; verbatim
(defun rcirc-handler-001 (process sender args text)
(rcirc-handler-generic process "001" sender args text)
- ;; set the real server name
(with-rcirc-process-buffer process
(setq rcirc-connecting nil)
(rcirc-reschedule-timeout process)
@@ -2049,9 +2286,9 @@ in this buffer.")
(if (string-match "^\C-a\\(.*\\)\C-a$" message)
(rcirc-handler-CTCP process target sender (match-string 1 message))
(rcirc-print process sender "PRIVMSG" target message t))
- ;; update nick timestamp
- (if (member target (rcirc-nick-channels process sender))
- (rcirc-put-nick-channel process sender target))))
+ ;; update nick linestamp
+ (with-current-buffer (rcirc-get-buffer process target t)
+ (rcirc-put-nick-channel process sender target rcirc-current-line))))
(defun rcirc-handler-NOTICE (process sender args text)
(let ((target (car args))
@@ -2076,21 +2313,29 @@ in this buffer.")
(defun rcirc-handler-JOIN (process sender args text)
(let ((channel (car args)))
- (rcirc-get-buffer-create process channel)
+ (with-current-buffer (rcirc-get-buffer-create process channel)
+ ;; when recently rejoining, restore the linestamp
+ (rcirc-put-nick-channel process sender channel
+ (let ((last-activity-lines
+ (rcirc-elapsed-lines process sender channel)))
+ (when (and last-activity-lines
+ (< last-activity-lines rcirc-omit-threshold))
+ (rcirc-last-line process sender channel)))))
+
(rcirc-print process sender "JOIN" channel "")
;; print in private chat buffer if it exists
(when (rcirc-get-buffer (rcirc-buffer-process) sender)
- (rcirc-print process sender "JOIN" sender channel))
-
- (rcirc-put-nick-channel process sender channel)))
+ (rcirc-print process sender "JOIN" sender channel))))
;; PART and KICK are handled the same way
(defun rcirc-handler-PART-or-KICK (process response channel sender nick args)
(rcirc-ignore-update-automatic nick)
(if (not (string= nick (rcirc-nick process)))
;; this is someone else leaving
- (rcirc-remove-nick-channel process nick channel)
+ (progn
+ (rcirc-maybe-remember-nick-quit process nick channel)
+ (rcirc-remove-nick-channel process nick channel))
;; this is us leaving
(mapc (lambda (n)
(rcirc-remove-nick-channel process n channel))
@@ -2099,8 +2344,7 @@ in this buffer.")
;; if the buffer is still around, make it inactive
(let ((buffer (rcirc-get-buffer process channel)))
(when buffer
- (with-current-buffer buffer
- (setq rcirc-target nil))))))
+ (rcirc-disconnect-buffer buffer)))))
(defun rcirc-handler-PART (process sender args text)
(let* ((channel (car args))
@@ -2125,16 +2369,30 @@ in this buffer.")
(rcirc-handler-PART-or-KICK process "KICK" channel sender nick reason)))
+(defun rcirc-maybe-remember-nick-quit (process nick channel)
+ "Remember NICK as leaving CHANNEL if they recently spoke."
+ (let ((elapsed-lines (rcirc-elapsed-lines process nick channel)))
+ (when (and elapsed-lines
+ (< elapsed-lines rcirc-omit-threshold))
+ (let ((buffer (rcirc-get-buffer process channel)))
+ (when buffer
+ (with-current-buffer buffer
+ (let ((record (assoc-string nick rcirc-recent-quit-alist t))
+ (line (rcirc-last-line process nick channel)))
+ (if record
+ (setcdr record line)
+ (setq rcirc-recent-quit-alist
+ (cons (cons nick line)
+ rcirc-recent-quit-alist))))))))))
+
(defun rcirc-handler-QUIT (process sender args text)
(rcirc-ignore-update-automatic sender)
(mapc (lambda (channel)
- (rcirc-print process sender "QUIT" channel (apply 'concat args)))
+ ;; broadcast quit message each channel
+ (rcirc-print process sender "QUIT" channel (apply 'concat args))
+ ;; record nick in quit table if they recently spoke
+ (rcirc-maybe-remember-nick-quit process sender channel))
(rcirc-nick-channels process sender))
-
- ;; print in private chat buffer if it exists
- (when (rcirc-get-buffer (rcirc-buffer-process) sender)
- (rcirc-print process sender "QUIT" sender (apply 'concat args)))
-
(rcirc-nick-remove process sender))
(defun rcirc-handler-NICK (process sender args text)
@@ -2169,7 +2427,7 @@ in this buffer.")
(when rcirc-auto-authenticate-flag (rcirc-authenticate))))))
(defun rcirc-handler-PING (process sender args text)
- (rcirc-send-string process (concat "PONG " (car args))))
+ (rcirc-send-string process (concat "PONG :" (car args))))
(defun rcirc-handler-PONG (process sender args text)
;; do nothing
@@ -2289,7 +2547,7 @@ Passwords are stored in `rcirc-authinfo' (which see)."
process
(concat
"PRIVMSG chanserv :identify "
- (cadr args) " " (car args))))
+ (car args) " " (cadr args))))
((equal method 'bitlbee)
(rcirc-send-string
process
@@ -2314,7 +2572,8 @@ Passwords are stored in `rcirc-authinfo' (which see)."
(format "%s sent unsupported ctcp: %s" sender text)
t)
(funcall handler process target sender args)
- (if (not (string= request "ACTION"))
+ (unless (or (string= request "ACTION")
+ (string= request "KEEPALIVE"))
(rcirc-print process sender "CTCP" target
(format "%s" text) t))))))