changeset 81387:c0681aed938d

(rcirc-format-response-string): Use rcirc-nick-syntax around bright and dim regexps. Make sure bright and dim matches use word anchors. Send text through rcirc-markup functions. (rcirc-url-regexp): Add single quote character. (rcirc-connect): Write logs to disk on auto-save-hook. Make server a non-optional argument. (rcirc-log-alist): New variable. (rcirc-log-directory): Make customizable. (rcirc-log-flag): New customizable variable. (rcirc-log): New function. (rcirc-print): Use above function. (rcirc-log-write): New function. (rcirc-generate-new-buffer-name): Strip text properties. (rcirc-switch-to-buffer-function): Remove variable. (rcirc-last-non-irc-buffer): Remove variable. (rcirc-non-irc-buffer): Add function. (rcirc-next-active-buffer): Use above function. (rcirc-keepalive): Send KEEPALIVE ctcp instead of a PING. (rcirc-handler-ctcp-KEEPALIVE): Add handler. (rcirc-handler-CTCP): Don't print KEEPALIVE responses. (rcirc-omit-mode): Add minor-mode. (rcirc-mode-map): Change C-c C-o binding. (rcirc-mode): Clear mode-line-process. Use a custom fill-paragraph-function. Set up buffer-invisibility-spec. (rcirc-response-formats): Remove timestamp code. (rcirc-omit-responses): Add variable. (rcirc-print): Don't put the overlay arrow on potentially omitted lines. Log line to disk. Record activity for private messages from /dim nicks. Facify the fill-prefix with rcirc-timestamp face. (rcirc-jump-to-first-unread-line): Print message if there is no unread text. (rcirc-clear-unread): New function. (rcirc-markup-text-functions): Add variable. (rcirc-markup-timestamp, rcirc-markup-fill): Add functions. (rcirc-debug): Don't mess with window configuration. (rcirc-send-message): Send message before printing locally. Add SILENT argument, do not print message if non-nil. (rcirc-visible-buffers): New function and variable. (rcirc-window-configuration-change-1): Add function. (rcirc-target-buffer): Make sure ACTIONs don't get sent to the server buffer. (rcirc-clean-up-buffer): Set rcirc-target to nil when finished. (rcirc-fill-paragraph): Add function. (rcirc-record-activity, rcirc-window-configuration-change-1): Only update the activity string if it has actually changed. (rcirc-update-activity-string): Remove padding characters from the mode-line string. (rcirc-disconnect-buffer): New function to be called when a channel is parted or the user quits. (rcirc-server-name): Warn when the server-name hasn't been set. (rcirc-window-configuration-change): Postpone work until post-command-hook. (rcirc-window-configuration-change-1): Update mode-line and overlay arrows here. (rcirc-authenticate): Fix chanserv identification. (rcirc-default-server): Remove variable. (rcirc): Connect according to rcirc-connections. (rcirc-connections): Add variable. (rcirc-startup-channels-alist): Remove variable. (rcirc-startup-channels): Remove function.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Wed, 13 Jun 2007 21:17:18 +0000
parents 44761a3f6c97
children 0a0d00f78651
files lisp/ChangeLog lisp/net/rcirc.el
diffstat 2 files changed, 518 insertions(+), 328 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Wed Jun 13 21:11:42 2007 +0000
+++ b/lisp/ChangeLog	Wed Jun 13 21:17:18 2007 +0000
@@ -1,3 +1,66 @@
+2007-06-13  Ryan Yeske  <rcyeske@gmail.com>
+
+	* rcirc.el (rcirc-format-response-string): Use rcirc-nick-syntax
+	around bright and dim regexps. Make sure bright and dim matches
+	use word anchors.  Send text through rcirc-markup functions.
+	(rcirc-url-regexp): Add single quote character.
+	(rcirc-connect): Write logs to disk on auto-save-hook.
+	Make server a non-optional argument.
+	(rcirc-log-alist): New variable.
+	(rcirc-log-directory): Make customizable.
+	(rcirc-log-flag): New customizable variable.
+	(rcirc-log): New function.
+	(rcirc-print): Use above function.
+	(rcirc-log-write): New function.
+	(rcirc-generate-new-buffer-name): Strip text properties.
+	(rcirc-switch-to-buffer-function): Remove variable.
+	(rcirc-last-non-irc-buffer): Remove variable.
+	(rcirc-non-irc-buffer): Add function.
+	(rcirc-next-active-buffer): Use above function.
+	(rcirc-keepalive): Send KEEPALIVE ctcp instead of a PING.
+	(rcirc-handler-ctcp-KEEPALIVE): Add handler.
+	(rcirc-handler-CTCP): Don't print KEEPALIVE responses.
+	(rcirc-omit-mode): Add minor-mode.
+	(rcirc-mode-map): Change C-c C-o binding.
+	(rcirc-mode): Clear mode-line-process. Use a custom
+	fill-paragraph-function.  Set up buffer-invisibility-spec.
+	(rcirc-response-formats): Remove timestamp code.
+	(rcirc-omit-responses): Add variable.
+	(rcirc-print): Don't put the overlay arrow on potentially omitted
+	lines.  Log line to disk.  Record activity for private messages
+	from /dim nicks.  Facify the fill-prefix with rcirc-timestamp face.
+	(rcirc-jump-to-first-unread-line): Print message if there is no
+	unread text.
+	(rcirc-clear-unread): New function.
+	(rcirc-markup-text-functions): Add variable.
+	(rcirc-markup-timestamp, rcirc-markup-fill): Add functions.
+	(rcirc-debug): Don't mess with window configuration.
+	(rcirc-send-message): Send message before printing locally.
+	Add SILENT argument, do not print message if non-nil.
+	(rcirc-visible-buffers): New function and variable.
+	(rcirc-window-configuration-change-1): Add function.
+	(rcirc-target-buffer): Make sure ACTIONs don't get sent to the
+	server buffer.
+	(rcirc-clean-up-buffer): Set rcirc-target to nil when finished.
+	(rcirc-fill-paragraph): Add function.
+	(rcirc-record-activity, rcirc-window-configuration-change-1):
+	Only update the activity string if it has actually changed.
+	(rcirc-update-activity-string): Remove padding characters from the
+	mode-line string.
+	(rcirc-disconnect-buffer): New function to be called when a
+	channel is parted or the user quits.
+	(rcirc-server-name): Warn when the server-name hasn't been set.
+	(rcirc-window-configuration-change): Postpone work until
+	post-command-hook.
+	(rcirc-window-configuration-change-1): Update mode-line and
+	overlay arrows here.
+	(rcirc-authenticate): Fix chanserv identification.
+	(rcirc-default-server): Remove variable.
+	(rcirc): Connect according to rcirc-connections.
+	(rcirc-connections): Add variable.
+	(rcirc-startup-channels-alist): Remove variable.
+	(rcirc-startup-channels): Remove function.
+
 2007-06-13  Stefan Monnier  <monnier@iro.umontreal.ca>
 
 	* diff-mode.el (diff-font-lock-keywords): Fix M. Kifer's last change.
--- a/lisp/net/rcirc.el	Wed Jun 13 21:11:42 2007 +0000
+++ b/lisp/net/rcirc.el	Wed Jun 13 21:17:18 2007 +0000
@@ -55,9 +55,24 @@
   :link '(custom-manual "(rcirc)")
   :group 'applications)
 
-(defcustom rcirc-default-server "irc.freenode.net"
-  "The default server to connect to."
-  :type 'string
+(defcustom rcirc-connections
+  '(("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.
+
+PARAMETERS is a plist of optional connection parameters.  Valid
+properties are: nick (a string), port (number or string),
+user-name (string), full-name (string), and channels (list of
+strings)."
+  :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
@@ -82,12 +97,6 @@
   :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 +104,9 @@
 
 (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 +127,11 @@
   "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 +157,8 @@
   :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)
 
@@ -319,36 +332,69 @@
 (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-connections'.
+
+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-connections
+				      nil nil
+				      (caar rcirc-connections)))
+	     (server-plist (cdr (assoc-string server rcirc-connections)))
+	     (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-user-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-connections'
+    (let (connected-servers)
+      (dolist (c rcirc-connections)
+	(let ((server (car c))
+	      (port (or (plist-get (cdr c) 'port) rcirc-default-port))
+	      (nick (or (plist-get (cdr c) 'nick) rcirc-default-nick))
+	      (user-name (or (plist-get (cdr c) 'user-name)
+			     rcirc-default-user-name))
+	      (full-name (or (plist-get (cdr c) 'full-name) 
+			     rcirc-default-user-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"
+		 (concat (mapconcat 'identity (butlast connected-servers) ", ")
+			 ", and " (car (last connected-servers))))))))
+
 ;;;###autoload
 (defalias 'irc 'rcirc)
 
@@ -365,7 +411,8 @@
 (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,7 +421,6 @@
 				(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))
@@ -412,6 +458,8 @@
       (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 +494,21 @@
       (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 +518,13 @@
 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 +542,16 @@
 			       (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)
@@ -593,7 +653,8 @@
 (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 +671,10 @@
 (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 +687,9 @@
          (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)
@@ -711,7 +774,7 @@
 (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 "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 +800,10 @@
 
 (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).")
+
 (defun rcirc-mode (process target)
   "Major mode for IRC channel buffers.
 
@@ -745,6 +812,7 @@
   (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,6 +824,8 @@
   (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-short-buffer-name)
   (setq rcirc-short-buffer-name nil)
@@ -785,6 +855,8 @@
   (setq overlay-arrow-position (make-marker))
   (set-marker overlay-arrow-position nil)
 
+  (setq buffer-invisibility-spec '(rcirc-ignored-user))
+
   ;; if the user changes the major mode or kills the buffer, there is
   ;; cleanup work to do
   (add-hook 'change-major-mode-hook 'rcirc-change-major-mode-hook nil t)
@@ -873,14 +945,16 @@
 	(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 +976,14 @@
 	  (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)
+	  new-buffer)))))
 
 (defun rcirc-send-input ()
   "Send input to target associated with the current buffer."
@@ -943,6 +1017,14 @@
 	  (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)
@@ -1021,7 +1103,6 @@
 (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 +1133,12 @@
 	(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 +1150,6 @@
   %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 +1162,67 @@
 		:value-type string)
   :group 'rcirc)
 
+(defcustom rcirc-omit-responses 
+  '("JOIN" "PART" "QUIT")
+  "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 +1232,8 @@
 	   (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,6 +1246,17 @@
 (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)
+
 (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,
@@ -1212,7 +1279,8 @@
 	    (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 +1290,40 @@
 	  (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 ((start (point)))
+	    (insert (rcirc-format-response-string process sender response nil 
+						  text)
+		    (propertize "\n" 'hard t))
 
-	    (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))
+ 	    ;; 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)))
 
-	      ;; 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)))))
+	    ;; 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)))
+		  (save-excursion (rcirc-markup-fill sender response)))
 
-	  ;; 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)))
+		(when rcirc-read-only-flag
+		  (add-text-properties (point-min) (point-max)
+				       '(read-only t front-sticky t))))
+	      ;; make text omittable
+	      (when (and (member response rcirc-omit-responses)
+			 (> start (point-min)))
+		(put-text-property (1- start) (1- rcirc-prompt-start-marker)
+				   'invisible 'rcirc-omit))))
+
+	  (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 +1339,26 @@
 					 (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 +1368,45 @@
 	(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))
+(defun rcirc-log (process sender response target text)
+  "Record line in `rcirc-log', to be later written to disk."
+  (let* ((filename (rcirc-generate-new-buffer-name process target))
+	 (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'."
+  (make-directory rcirc-log-directory t)
+  (dolist (cell rcirc-log-alist)
+    (with-temp-buffer
+      (insert (cdr cell))
+      (write-region (point-min) (point-max)
+		    (concat rcirc-log-directory "/" (car cell))
+		    t 'quiet)))
+  (setq rcirc-log-alist nil))
 
 (defun rcirc-join-channels (process channels)
   "Join CHANNELS."
@@ -1437,6 +1523,9 @@
 (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,48 +1547,59 @@
 	     "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))
+  (let ((line (1- (count-screen-lines (point) (window-start)))))
+    (if rcirc-omit-mode
+	(progn
+	  (add-to-invisibility-spec 'rcirc-omit)
+	  (message "Rcirc-Omit mode enabled"))
+      (remove-from-invisibility-spec 'rcirc-omit)
+      (message "Rcirc-Omit mode disabled"))
+    (recenter line))
+  (force-mode-line-update))
 
 (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)))
+  (if (marker-position overlay-arrow-position)
+      (goto-char overlay-arrow-position)
+    (message "No unread messages")))
 
-(defvar rcirc-last-non-irc-buffer nil
-  "The buffer to switch to when there is no more activity.")
+(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))
 	 (hipri (cdr pair)))
     (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)) t)
       (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))
+	  (switch-to-buffer (rcirc-non-irc-buffer))
 	(message (concat
 		  "No IRC activity."
 		  (when lopri
@@ -1518,15 +1618,19 @@
 (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)
@@ -1535,6 +1639,12 @@
   (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 +1656,9 @@
 	  (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 +1667,18 @@
 	 (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 +1698,47 @@
   (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)))))
+		    (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))
 
-  ;; 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)))
+    (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 +1848,9 @@
                                          (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 +2029,7 @@
 		  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.")
 
@@ -1932,38 +2059,25 @@
 
 
 (defvar rcirc-markup-text-functions
-  '(rcirc-markup-body-text
-    rcirc-markup-attributes
+  '(rcirc-markup-attributes
     rcirc-markup-my-nick
     rcirc-markup-urls
     rcirc-markup-keywords
-    rcirc-markup-bright-nicks)
+    rcirc-markup-bright-nicks
+    rcirc-markup-fill)
+    
   "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.")
+Each function takes two arguments, SENDER, 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-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-timestamp (sender response)
+  (goto-char (point-min))
+  (insert (rcirc-facify (format-time-string rcirc-time-format) 
+			'rcirc-timestamp)))
 
-(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 +2093,21 @@
   (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))
+    (while (re-search-forward (concat "\\b" 
+				      (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 +2115,41 @@
       (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))))))
+      (push (buffer-substring-no-properties start end) 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-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 (process sender response channel-buffer)
+(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))))
+      (fill-region (point) (point-max) nil t))))
 
 ;;; handlers
 ;; these are called with the server PROCESS, the SENDER, which is a
@@ -2099,8 +2226,7 @@
     ;; 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))
@@ -2169,7 +2295,7 @@
         (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 +2415,7 @@
 		  process
 		  (concat
 		   "PRIVMSG chanserv :identify "
-		   (cadr args) " " (car args))))
+		   (car args) " " (cadr args))))
 		((equal method 'bitlbee)
 		 (rcirc-send-string
 		  process
@@ -2314,7 +2440,8 @@
                          (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))))))