Cleanup uses of "-hooks".
[bpt/emacs.git] / lisp / net / rcirc.el
CommitLineData
bd43c990
RS
1;;; rcirc.el --- default, simple IRC client.
2
e1ac4066 3;; Copyright (C) 2005-2012 Free Software Foundation, Inc.
bd43c990 4
9cf56b2c
DD
5;; Author: Ryan Yeske <rcyeske@gmail.com>
6;; Maintainers: Ryan Yeske <rcyeske@gmail.com>,
b87a8200 7;; Deniz Dogan <deniz@dogan.se>
bd43c990
RS
8;; Keywords: comm
9
b71cef5c 10;; This file is part of GNU Emacs.
bd43c990 11
874a927a 12;; GNU Emacs is free software: you can redistribute it and/or modify
bd43c990 13;; it under the terms of the GNU General Public License as published by
874a927a
GM
14;; the Free Software Foundation, either version 3 of the License, or
15;; (at your option) any later version.
bd43c990 16
874a927a 17;; GNU Emacs is distributed in the hope that it will be useful,
bd43c990 18;; but WITHOUT ANY WARRANTY; without even the implied warranty of
92c4adc1 19;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
bd43c990
RS
20;; GNU General Public License for more details.
21
22;; You should have received a copy of the GNU General Public License
874a927a 23;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
bd43c990
RS
24
25;;; Commentary:
26
adf794e4
EZ
27;; Internet Relay Chat (IRC) is a form of instant communication over
28;; the Internet. It is mainly designed for group (many-to-many)
29;; communication in discussion forums called channels, but also allows
30;; one-to-one communication.
31
fffa137c 32;; Rcirc has simple defaults and clear and consistent behavior.
37269466 33;; Message arrival timestamps, activity notification on the mode line,
adf794e4
EZ
34;; message filling, nick completion, and keepalive pings are all
35;; enabled by default, but can easily be adjusted or turned off. Each
36;; discussion takes place in its own buffer and there is a single
37;; server buffer per connection.
bd43c990 38
bd43c990
RS
39;; Open a new irc connection with:
40;; M-x irc RET
41
7faa3f8c
MB
42;;; Todo:
43
bd43c990
RS
44;;; Code:
45
46(require 'ring)
47(require 'time-date)
48(eval-when-compile (require 'cl))
49
adf794e4
EZ
50(defgroup rcirc nil
51 "Simple IRC client."
52 :version "22.1"
2fbed782 53 :prefix "rcirc-"
e8f10ddb 54 :link '(custom-manual "(rcirc)")
adf794e4
EZ
55 :group 'applications)
56
0ffab1eb 57(defcustom rcirc-server-alist
488086f4
SM
58 '(("irc.freenode.net" :channels ("#rcirc")
59 ;; Don't use the TLS port by default, in case gnutls is not available.
60 ;; :port 7000 :encryption tls
61 ))
195eca78
SM
62 "An alist of IRC connections to establish when running `rcirc'.
63Each element looks like (SERVER-NAME PARAMETERS).
64
65SERVER-NAME is a string describing the server to connect
66to.
67
0ffab1eb
TTN
68The optional PARAMETERS come in pairs PARAMETER VALUE.
69
70The following parameters are recognized:
71
72`:nick'
73
74VALUE must be a string. If absent, `rcirc-default-nick' is used
75for this connection.
76
77`:port'
78
79VALUE must be a number or string. If absent,
80`rcirc-default-port' is used.
81
82`:user-name'
83
84VALUE must be a string. If absent, `rcirc-default-user-name' is
85used.
86
a71832f7
SM
87`:password'
88
89VALUE must be a string. If absent, no PASS command will be sent
90to the server.
91
0ffab1eb
TTN
92`:full-name'
93
94VALUE must be a string. If absent, `rcirc-default-full-name' is
95used.
96
97`:channels'
98
99VALUE must be a list of strings describing which channels to join
100when connecting to this server. If absent, no channels will be
488086f4
SM
101connected to automatically.
102
103`:encryption'
104
105VALUE must be `plain' (the default) for unencrypted connections, or `tls'
106for connections using SSL/TLS."
0ffab1eb 107 :type '(alist :key-type string
488086f4
SM
108 :value-type (plist :options
109 ((:nick string)
110 (:port integer)
111 (:user-name string)
112 (:password string)
113 (:full-name string)
114 (:channels (repeat string))
115 (:encryption (choice (const tls)
116 (const plain))))))
adf794e4 117 :group 'rcirc)
bd43c990 118
a2524d26 119(defcustom rcirc-default-port 6667
adf794e4
EZ
120 "The default port to connect to."
121 :type 'integer
122 :group 'rcirc)
bd43c990 123
a2524d26 124(defcustom rcirc-default-nick (user-login-name)
adf794e4
EZ
125 "Your nick."
126 :type 'string
127 :group 'rcirc)
bd43c990 128
d26781af 129(defcustom rcirc-default-user-name "user"
adf794e4 130 "Your user name sent to the server when connecting."
6169260b 131 :version "24.1" ; changed default
adf794e4
EZ
132 :type 'string
133 :group 'rcirc)
bd43c990 134
d26781af 135(defcustom rcirc-default-full-name "unknown"
adf794e4 136 "The full name sent to the server when connecting."
6169260b 137 :version "24.1" ; changed default
adf794e4
EZ
138 :type 'string
139 :group 'rcirc)
bd43c990 140
adf794e4 141(defcustom rcirc-fill-flag t
fb7ada5f 142 "Non-nil means line-wrap messages printed in channel buffers."
adf794e4
EZ
143 :type 'boolean
144 :group 'rcirc)
bd43c990 145
adf794e4 146(defcustom rcirc-fill-column nil
fb7ada5f 147 "Column beyond which automatic line-wrapping should happen.
195eca78
SM
148If nil, use value of `fill-column'. If 'frame-width, use the
149maximum frame width."
adf794e4
EZ
150 :type '(choice (const :tag "Value of `fill-column'")
151 (const :tag "Full frame width" frame-width)
152 (integer :tag "Number of columns"))
153 :group 'rcirc)
bd43c990 154
adf794e4 155(defcustom rcirc-fill-prefix nil
fb7ada5f 156 "Text to insert before filled lines.
bd43c990 157If nil, calculate the prefix dynamically to line up text
adf794e4
EZ
158underneath each nick."
159 :type '(choice (const :tag "Dynamic" nil)
160 (string :tag "Prefix text"))
161 :group 'rcirc)
bd43c990 162
adf794e4
EZ
163(defvar rcirc-ignore-buffer-activity-flag nil
164 "If non-nil, ignore activity in this buffer.")
165(make-variable-buffer-local 'rcirc-ignore-buffer-activity-flag)
bd43c990 166
a2524d26
EZ
167(defvar rcirc-low-priority-flag nil
168 "If non-nil, activity in this buffer is considered low priority.")
169(make-variable-buffer-local 'rcirc-low-priority-flag)
170
195eca78
SM
171(defvar rcirc-omit-mode nil
172 "Non-nil if Rcirc-Omit mode is enabled.
173Use the command `rcirc-omit-mode' to change this variable.")
174(make-variable-buffer-local 'rcirc-omit-mode)
175
adf794e4 176(defcustom rcirc-time-format "%H:%M "
fb7ada5f 177 "Describes how timestamps are printed.
adf794e4
EZ
178Used as the first arg to `format-time-string'."
179 :type 'string
180 :group 'rcirc)
bd43c990 181
adf794e4 182(defcustom rcirc-input-ring-size 1024
fb7ada5f 183 "Size of input history ring."
adf794e4
EZ
184 :type 'integer
185 :group 'rcirc)
bd43c990 186
adf794e4 187(defcustom rcirc-read-only-flag t
fb7ada5f 188 "Non-nil means make text in IRC buffers read-only."
adf794e4
EZ
189 :type 'boolean
190 :group 'rcirc)
bd43c990 191
adf794e4 192(defcustom rcirc-buffer-maximum-lines nil
fb7ada5f 193 "The maximum size in lines for rcirc buffers.
bd43c990 194Channel buffers are truncated from the top to be no greater than this
92c4adc1 195number. If zero or nil, no truncating is done."
adf794e4
EZ
196 :type '(choice (const :tag "No truncation" nil)
197 (integer :tag "Number of lines"))
198 :group 'rcirc)
bd43c990 199
d40ac716 200(defcustom rcirc-scroll-show-maximum-output t
fb7ada5f 201 "If non-nil, scroll buffer to keep the point at the bottom of
195eca78 202the window."
f8db61b2
EZ
203 :type 'boolean
204 :group 'rcirc)
7faa3f8c 205
db58efbf
EZ
206(defcustom rcirc-authinfo nil
207 "List of authentication passwords.
208Each element of the list is a list with a SERVER-REGEXP string
209and a method symbol followed by method specific arguments.
210
211The valid METHOD symbols are `nickserv', `chanserv' and
212`bitlbee'.
bd43c990 213
d7a0fd6f
GM
214The ARGUMENTS for each METHOD symbol are:
215 `nickserv': NICK PASSWORD [NICKSERV-NICK]
db58efbf
EZ
216 `chanserv': NICK CHANNEL PASSWORD
217 `bitlbee': NICK PASSWORD
77f63d30 218 `quakenet': ACCOUNT PASSWORD
bd43c990 219
d7a0fd6f 220Examples:
db58efbf
EZ
221 ((\"freenode\" nickserv \"bob\" \"p455w0rd\")
222 (\"freenode\" chanserv \"bob\" \"#bobland\" \"passwd99\")
d7a0fd6f 223 (\"bitlbee\" bitlbee \"robert\" \"sekrit\")
77f63d30
DD
224 (\"dal.net\" nickserv \"bob\" \"sekrit\" \"NickServ@services.dal.net\")
225 (\"quakenet.org\" quakenet \"bobby\" \"sekrit\"))"
db58efbf
EZ
226 :type '(alist :key-type (string :tag "Server")
227 :value-type (choice (list :tag "NickServ"
228 (const nickserv)
229 (string :tag "Nick")
230 (string :tag "Password"))
231 (list :tag "ChanServ"
232 (const chanserv)
233 (string :tag "Nick")
234 (string :tag "Channel")
235 (string :tag "Password"))
236 (list :tag "BitlBee"
237 (const bitlbee)
238 (string :tag "Nick")
77f63d30
DD
239 (string :tag "Password"))
240 (list :tag "QuakeNet"
241 (const quakenet)
242 (string :tag "Account")
243 (string :tag "Password"))))
adf794e4 244 :group 'rcirc)
bd43c990 245
db58efbf 246(defcustom rcirc-auto-authenticate-flag t
fb7ada5f 247 "Non-nil means automatically send authentication string to server.
db58efbf 248See also `rcirc-authinfo'."
adf794e4
EZ
249 :type 'boolean
250 :group 'rcirc)
bd43c990 251
72d2c2e3 252(defcustom rcirc-authenticate-before-join t
fb7ada5f 253 "Non-nil means authenticate to services before joining channels.
72d2c2e3
DD
254Currently only works with NickServ on some networks."
255 :version "24.1"
256 :type 'boolean
257 :group 'rcirc)
258
adf794e4 259(defcustom rcirc-prompt "> "
2e398771 260 "Prompt string to use in IRC buffers.
bd43c990
RS
261
262The following replacements are made:
263%n is your nick.
264%s is the server.
265%t is the buffer target, a channel or a user.
266
adf794e4
EZ
267Setting this alone will not affect the prompt;
268use either M-x customize or also call `rcirc-update-prompt'."
269 :type 'string
270 :set 'rcirc-set-changed
271 :initialize 'custom-initialize-default
272 :group 'rcirc)
273
f8db61b2
EZ
274(defcustom rcirc-keywords nil
275 "List of keywords to highlight in message text."
276 :type '(repeat string)
277 :group 'rcirc)
278
2c8abe90
AS
279(defcustom rcirc-ignore-list ()
280 "List of ignored nicks.
281Use /ignore to list them, use /ignore NICK to add or remove a nick."
282 :type '(repeat string)
283 :group 'rcirc)
284
285(defvar rcirc-ignore-list-automatic ()
286 "List of ignored nicks added to `rcirc-ignore-list' because of renaming.
287When an ignored person renames, their nick is added to both lists.
288Nicks will be removed from the automatic list on follow-up renamings or
289parts.")
290
f8db61b2
EZ
291(defcustom rcirc-bright-nicks nil
292 "List of nicks to be emphasized.
02f47e86 293See `rcirc-bright-nick' face."
f8db61b2 294 :type '(repeat string)
02f47e86
MB
295 :group 'rcirc)
296
f8db61b2
EZ
297(defcustom rcirc-dim-nicks nil
298 "List of nicks to be deemphasized.
02f47e86 299See `rcirc-dim-nick' face."
f8db61b2 300 :type '(repeat string)
02f47e86
MB
301 :group 'rcirc)
302
d1069532
SM
303(define-obsolete-variable-alias 'rcirc-print-hooks
304 'rcirc-print-functions "24.3")
305(defcustom rcirc-print-functions nil
adf794e4
EZ
306 "Hook run after text is printed.
307Called with 5 arguments, PROCESS, SENDER, RESPONSE, TARGET and TEXT."
308 :type 'hook
309 :group 'rcirc)
bd43c990 310
72d2c2e3
DD
311(defvar rcirc-authenticated-hook nil
312 "Hook run after successfully authenticated.")
313
db58efbf
EZ
314(defcustom rcirc-always-use-server-buffer-flag nil
315 "Non-nil means messages without a channel target will go to the server buffer."
316 :type 'boolean
317 :group 'rcirc)
318
108bf785 319(defcustom rcirc-decode-coding-system 'utf-8
5ab33f2b 320 "Coding system used to decode incoming irc messages.
108bf785
LL
321Set to 'undecided if you want the encoding of the incoming
322messages autodetected."
a2524d26
EZ
323 :type 'coding-system
324 :group 'rcirc)
325
326(defcustom rcirc-encode-coding-system 'utf-8
327 "Coding system used to encode outgoing irc messages."
328 :type 'coding-system
329 :group 'rcirc)
330
331(defcustom rcirc-coding-system-alist nil
f8db61b2 332 "Alist to decide a coding system to use for a channel I/O operation.
a2524d26
EZ
333The format is ((PATTERN . VAL) ...).
334PATTERN is either a string or a cons of strings.
335If PATTERN is a string, it is used to match a target.
336If PATTERN is a cons of strings, the car part is used to match a
337target, and the cdr part is used to match a server.
338VAL is either a coding system or a cons of coding systems.
339If VAL is a coding system, it is used for both decoding and encoding
340messages.
341If VAL is a cons of coding systems, the car part is used for decoding,
342and the cdr part is used for encoding."
343 :type '(alist :key-type (choice (string :tag "Channel Regexp")
344 (cons (string :tag "Channel Regexp")
345 (string :tag "Server Regexp")))
346 :value-type (choice coding-system
347 (cons (coding-system :tag "Decode")
348 (coding-system :tag "Encode"))))
349 :group 'rcirc)
350
351(defcustom rcirc-multiline-major-mode 'fundamental-mode
352 "Major-mode function to use in multiline edit buffers."
353 :type 'function
354 :group 'rcirc)
355
2a4466ca
DD
356(defcustom rcirc-nick-completion-format "%s: "
357 "Format string to use in nick completions.
358
359The format string is only used when completing at the beginning
360of a line. The string is passed as the first argument to
361`format' with the nickname as the second argument."
2d7d6439 362 :version "24.1"
2a4466ca
DD
363 :type 'string
364 :group 'rcirc)
365
a63067fc
DD
366(defcustom rcirc-kill-channel-buffers nil
367 "When non-nil, kill channel buffers when the server buffer is killed.
368Only the channel buffers associated with the server in question
369will be killed."
2a1e2476 370 :version "24.3"
a63067fc
DD
371 :type 'boolean
372 :group 'rcirc)
373
a2524d26
EZ
374(defvar rcirc-nick nil)
375
bd43c990
RS
376(defvar rcirc-prompt-start-marker nil)
377(defvar rcirc-prompt-end-marker nil)
378
379(defvar rcirc-nick-table nil)
380
a0a5c583
GM
381(defvar rcirc-recent-quit-alist nil
382 "Alist of nicks that have recently quit or parted the channel.")
383
2c8abe90
AS
384(defvar rcirc-nick-syntax-table
385 (let ((table (make-syntax-table text-mode-syntax-table)))
386 (mapc (lambda (c) (modify-syntax-entry c "w" table))
387 "[]\\`_^{|}-")
388 (modify-syntax-entry ?' "_" table)
389 table)
390 "Syntax table which includes all nick characters as word constituents.")
391
adf794e4
EZ
392;; each process has an alist of (target . buffer) pairs
393(defvar rcirc-buffer-alist nil)
394
bd43c990 395(defvar rcirc-activity nil
a2524d26 396 "List of buffers with unviewed activity.")
bd43c990
RS
397
398(defvar rcirc-activity-string ""
37269466 399 "String displayed in mode line representing `rcirc-activity'.")
bd43c990
RS
400(put 'rcirc-activity-string 'risky-local-variable t)
401
a2524d26
EZ
402(defvar rcirc-server-buffer nil
403 "The server buffer associated with this channel buffer.")
bd43c990
RS
404
405(defvar rcirc-target nil
406 "The channel or user associated with this buffer.")
407
bd43c990
RS
408(defvar rcirc-urls nil
409 "List of urls seen in the current buffer.")
7faa3f8c 410(put 'rcirc-urls 'permanent-local t)
bd43c990 411
2e875089 412(defvar rcirc-timeout-seconds 600
8216fbaf 413 "Kill connection after this many seconds if there is no activity.")
bd43c990 414
adf794e4 415(defconst rcirc-id-string (concat "rcirc on GNU Emacs " emacs-version))
bd43c990 416\f
bd43c990 417(defvar rcirc-startup-channels nil)
195eca78 418
c0db3477
CY
419(defvar rcirc-server-name-history nil
420 "History variable for \\[rcirc] call.")
421
422(defvar rcirc-server-port-history nil
423 "History variable for \\[rcirc] call.")
424
425(defvar rcirc-nick-name-history nil
426 "History variable for \\[rcirc] call.")
427
d26781af
RY
428(defvar rcirc-user-name-history nil
429 "History variable for \\[rcirc] call.")
430
bd43c990 431;;;###autoload
db58efbf 432(defun rcirc (arg)
0ffab1eb 433 "Connect to all servers in `rcirc-server-alist'.
195eca78
SM
434
435Do not connect to a server if it is already connected.
436
437If ARG is non-nil, instead prompt for connection parameters."
db58efbf
EZ
438 (interactive "P")
439 (if arg
0ffab1eb
TTN
440 (let* ((server (completing-read "IRC Server: "
441 rcirc-server-alist
195eca78 442 nil nil
c0db3477
CY
443 (caar rcirc-server-alist)
444 'rcirc-server-name-history))
0ffab1eb
TTN
445 (server-plist (cdr (assoc-string server rcirc-server-alist)))
446 (port (read-string "IRC Port: "
195eca78 447 (number-to-string
d8937064 448 (or (plist-get server-plist :port)
c0db3477
CY
449 rcirc-default-port))
450 'rcirc-server-port-history))
195eca78 451 (nick (read-string "IRC Nick: "
d8937064 452 (or (plist-get server-plist :nick)
c0db3477
CY
453 rcirc-default-nick)
454 'rcirc-nick-name-history))
d26781af
RY
455 (user-name (read-string "IRC Username: "
456 (or (plist-get server-plist :user-name)
457 rcirc-default-user-name)
458 'rcirc-user-name-history))
a71832f7
SM
459 (password (read-passwd "IRC Password: " nil
460 (plist-get server-plist :password)))
db58efbf
EZ
461 (channels (split-string
462 (read-string "IRC Channels: "
0ffab1eb 463 (mapconcat 'identity
195eca78 464 (plist-get server-plist
d8937064 465 :channels)
195eca78 466 " "))
488086f4 467 "[, ]+" t))
ac09b8a1 468 (encryption (rcirc-prompt-for-encryption server-plist)))
a71832f7 469 (rcirc-connect server port nick user-name
0ffab1eb 470 rcirc-default-full-name
488086f4 471 channels password encryption))
0ffab1eb 472 ;; connect to servers in `rcirc-server-alist'
195eca78 473 (let (connected-servers)
0ffab1eb 474 (dolist (c rcirc-server-alist)
195eca78 475 (let ((server (car c))
0ffab1eb
TTN
476 (nick (or (plist-get (cdr c) :nick) rcirc-default-nick))
477 (port (or (plist-get (cdr c) :port) rcirc-default-port))
478 (user-name (or (plist-get (cdr c) :user-name)
195eca78 479 rcirc-default-user-name))
0ffab1eb
TTN
480 (full-name (or (plist-get (cdr c) :full-name)
481 rcirc-default-full-name))
a71832f7 482 (channels (plist-get (cdr c) :channels))
488086f4 483 (password (plist-get (cdr c) :password))
d64a438f
LL
484 (encryption (plist-get (cdr c) :encryption))
485 contact)
195eca78
SM
486 (when server
487 (let (connected)
488 (dolist (p (rcirc-process-list))
489 (when (string= server (process-name p))
490 (setq connected p)))
491 (if (not connected)
492 (condition-case e
a71832f7 493 (rcirc-connect server port nick user-name
488086f4 494 full-name channels password encryption)
0ffab1eb 495 (quit (message "Quit connecting to %s" server)))
195eca78 496 (with-current-buffer (process-buffer connected)
d64a438f
LL
497 (setq contact (process-contact
498 (get-buffer-process (current-buffer)) :host))
499 (setq connected-servers
500 (cons (if (stringp contact) contact server)
501 connected-servers))))))))
195eca78
SM
502 (when connected-servers
503 (message "Already connected to %s"
a0a5c583
GM
504 (if (cdr connected-servers)
505 (concat (mapconcat 'identity (butlast connected-servers) ", ")
506 ", and "
507 (car (last connected-servers)))
508 (car connected-servers)))))))
195eca78 509
bd43c990
RS
510;;;###autoload
511(defalias 'irc 'rcirc)
512
513\f
514(defvar rcirc-process-output nil)
bd43c990
RS
515(defvar rcirc-topic nil)
516(defvar rcirc-keepalive-timer nil)
ad8121fe 517(defvar rcirc-last-server-message-time nil)
8216fbaf
EZ
518(defvar rcirc-server nil) ; server provided by server
519(defvar rcirc-server-name nil) ; server name given by 001 response
520(defvar rcirc-timeout-timer nil)
9882e214 521(defvar rcirc-user-authenticated nil)
8216fbaf
EZ
522(defvar rcirc-user-disconnect nil)
523(defvar rcirc-connecting nil)
524(defvar rcirc-process nil)
8d214091
RF
525
526;;;###autoload
a71832f7 527(defun rcirc-connect (server &optional port nick user-name
488086f4 528 full-name startup-channels password encryption)
bd43c990
RS
529 (save-excursion
530 (message "Connecting to %s..." server)
531 (let* ((inhibit-eol-conversion)
2fbed782
EZ
532 (port-number (if port
533 (if (stringp port)
534 (string-to-number port)
535 port)
a2524d26 536 rcirc-default-port))
a2524d26
EZ
537 (nick (or nick rcirc-default-nick))
538 (user-name (or user-name rcirc-default-user-name))
0ffab1eb 539 (full-name (or full-name rcirc-default-full-name))
a2524d26 540 (startup-channels startup-channels)
488086f4
SM
541 (process (open-network-stream
542 server nil server port-number
543 :type (or encryption 'plain))))
bd43c990
RS
544 ;; set up process
545 (set-process-coding-system process 'raw-text 'raw-text)
adf794e4 546 (switch-to-buffer (rcirc-generate-new-buffer-name process nil))
bd43c990 547 (set-process-buffer process (current-buffer))
bd43c990 548 (rcirc-mode process nil)
a2524d26
EZ
549 (set-process-sentinel process 'rcirc-sentinel)
550 (set-process-filter process 'rcirc-filter)
488086f4
SM
551
552 (set (make-local-variable 'rcirc-process) process)
553 (set (make-local-variable 'rcirc-server) server)
554 (set (make-local-variable 'rcirc-server-name) server) ; Update when we get 001 response.
555 (set (make-local-variable 'rcirc-buffer-alist) nil)
556 (set (make-local-variable 'rcirc-nick-table)
557 (make-hash-table :test 'equal))
558 (set (make-local-variable 'rcirc-nick) nick)
559 (set (make-local-variable 'rcirc-process-output) nil)
560 (set (make-local-variable 'rcirc-startup-channels) startup-channels)
561 (set (make-local-variable 'rcirc-last-server-message-time)
562 (current-time))
563
564 (set (make-local-variable 'rcirc-timeout-timer) nil)
565 (set (make-local-variable 'rcirc-user-disconnect) nil)
566 (set (make-local-variable 'rcirc-user-authenticated) nil)
567 (set (make-local-variable 'rcirc-connecting) t)
bd43c990 568
195eca78
SM
569 (add-hook 'auto-save-hook 'rcirc-log-write)
570
bd43c990 571 ;; identify
fa7062f6 572 (unless (zerop (length password))
a71832f7 573 (rcirc-send-string process (concat "PASS " password)))
bd43c990
RS
574 (rcirc-send-string process (concat "NICK " nick))
575 (rcirc-send-string process (concat "USER " user-name
d26781af 576 " 0 * :" full-name))
bd43c990
RS
577
578 ;; setup ping timer if necessary
8216fbaf
EZ
579 (unless rcirc-keepalive-timer
580 (setq rcirc-keepalive-timer
581 (run-at-time 0 (/ rcirc-timeout-seconds 2) 'rcirc-keepalive)))
bd43c990
RS
582
583 (message "Connecting to %s...done" server)
584
585 ;; return process object
586 process)))
587
adf794e4
EZ
588(defmacro with-rcirc-process-buffer (process &rest body)
589 (declare (indent 1) (debug t))
590 `(with-current-buffer (process-buffer ,process)
591 ,@body))
592
a2524d26
EZ
593(defmacro with-rcirc-server-buffer (&rest body)
594 (declare (indent 0) (debug t))
595 `(with-current-buffer rcirc-server-buffer
596 ,@body))
597
73057ba9
DD
598(defun rcirc-float-time ()
599 (if (featurep 'xemacs)
600 (time-to-seconds (current-time))
601 (float-time)))
602
ac09b8a1
DD
603(defun rcirc-prompt-for-encryption (server-plist)
604 "Prompt the user for the encryption method to use.
605SERVER-PLIST is the property list for the server."
606 (let ((msg "Encryption (default %s): ")
607 (choices '("plain" "tls"))
608 (default (or (plist-get server-plist :encryption)
7e821d0d 609 'plain)))
ac09b8a1
DD
610 (intern
611 (completing-read (format msg default)
7e821d0d 612 choices nil t nil nil (symbol-name default)))))
ac09b8a1 613
bd43c990 614(defun rcirc-keepalive ()
ad8121fe
EZ
615 "Send keep alive pings to active rcirc processes.
616Kill processes that have not received a server message since the
617last ping."
bd43c990
RS
618 (if (rcirc-process-list)
619 (mapc (lambda (process)
8216fbaf
EZ
620 (with-rcirc-process-buffer process
621 (when (not rcirc-connecting)
1be1d1e9
DD
622 (rcirc-send-ctcp process
623 rcirc-nick
624 (format "KEEPALIVE %f"
73057ba9 625 (rcirc-float-time))))))
bd43c990 626 (rcirc-process-list))
8216fbaf 627 ;; no processes, clean up timer
bd43c990
RS
628 (cancel-timer rcirc-keepalive-timer)
629 (setq rcirc-keepalive-timer nil)))
630
195eca78
SM
631(defun rcirc-handler-ctcp-KEEPALIVE (process target sender message)
632 (with-rcirc-process-buffer process
73057ba9 633 (setq header-line-format (format "%f" (- (rcirc-float-time)
195eca78
SM
634 (string-to-number message))))))
635
3767e706 636(defvar rcirc-debug-buffer "*rcirc debug*")
adf794e4
EZ
637(defvar rcirc-debug-flag nil
638 "If non-nil, write information to `rcirc-debug-buffer'.")
639(defun rcirc-debug (process text)
bd43c990 640 "Add an entry to the debug log including PROCESS and TEXT.
2e398771 641Debug text is written to `rcirc-debug-buffer' if `rcirc-debug-flag'
adf794e4
EZ
642is non-nil."
643 (when rcirc-debug-flag
9a529312 644 (with-current-buffer (get-buffer-create rcirc-debug-buffer)
195eca78
SM
645 (goto-char (point-max))
646 (insert (concat
647 "["
648 (format-time-string "%Y-%m-%dT%T ") (process-name process)
649 "] "
650 text)))))
adf794e4 651
d1069532
SM
652(define-obsolete-variable-alias 'rcirc-sentinel-hooks
653 'rcirc-sentinel-functions "24.3")
654(defvar rcirc-sentinel-functions nil
bd43c990
RS
655 "Hook functions called when the process sentinel is called.
656Functions are called with PROCESS and SENTINEL arguments.")
657
658(defun rcirc-sentinel (process sentinel)
659 "Called when PROCESS receives SENTINEL."
660 (let ((sentinel (replace-regexp-in-string "\n" "" sentinel)))
adf794e4
EZ
661 (rcirc-debug process (format "SENTINEL: %S %S\n" process sentinel))
662 (with-rcirc-process-buffer process
663 (dolist (buffer (cons nil (mapcar 'cdr rcirc-buffer-alist)))
adf794e4 664 (with-current-buffer (or buffer (current-buffer))
db58efbf
EZ
665 (rcirc-print process "rcirc.el" "ERROR" rcirc-target
666 (format "%s: %s (%S)"
667 (process-name process)
668 sentinel
8216fbaf 669 (process-status process)) (not rcirc-target))
195eca78 670 (rcirc-disconnect-buffer)))
d1069532 671 (run-hook-with-args 'rcirc-sentinel-functions process sentinel))))
bd43c990 672
195eca78
SM
673(defun rcirc-disconnect-buffer (&optional buffer)
674 (with-current-buffer (or buffer (current-buffer))
675 ;; set rcirc-target to nil for each channel so cleanup
fe7a3057 676 ;; doesn't happen when we reconnect
195eca78 677 (setq rcirc-target nil)
0ffab1eb 678 (setq mode-line-process ":disconnected")))
195eca78 679
bd43c990
RS
680(defun rcirc-process-list ()
681 "Return a list of rcirc processes."
682 (let (ps)
683 (mapc (lambda (p)
18aa2c90 684 (when (buffer-live-p (process-buffer p))
adf794e4 685 (with-rcirc-process-buffer p
bd43c990
RS
686 (when (eq major-mode 'rcirc-mode)
687 (setq ps (cons p ps))))))
688 (process-list))
689 ps))
690
d1069532
SM
691(define-obsolete-variable-alias 'rcirc-receive-message-hooks
692 'rcirc-receive-message-functions "24.3")
693(defvar rcirc-receive-message-functions nil
2e398771
JB
694 "Hook functions run when a message is received from server.
695Function is called with PROCESS, COMMAND, SENDER, ARGS and LINE.")
bd43c990
RS
696(defun rcirc-filter (process output)
697 "Called when PROCESS receives OUTPUT."
adf794e4 698 (rcirc-debug process output)
8216fbaf 699 (rcirc-reschedule-timeout process)
adf794e4 700 (with-rcirc-process-buffer process
ad8121fe 701 (setq rcirc-last-server-message-time (current-time))
bd43c990
RS
702 (setq rcirc-process-output (concat rcirc-process-output output))
703 (when (= (aref rcirc-process-output
704 (1- (length rcirc-process-output))) ?\n)
705 (mapc (lambda (line)
706 (rcirc-process-server-response process line))
adf794e4 707 (split-string rcirc-process-output "[\n\r]" t))
bd43c990
RS
708 (setq rcirc-process-output nil))))
709
8216fbaf
EZ
710(defun rcirc-reschedule-timeout (process)
711 (with-rcirc-process-buffer process
712 (when (not rcirc-connecting)
713 (with-rcirc-process-buffer process
714 (when rcirc-timeout-timer (cancel-timer rcirc-timeout-timer))
715 (setq rcirc-timeout-timer (run-at-time rcirc-timeout-seconds nil
716 'rcirc-delete-process
717 process))))))
718
719(defun rcirc-delete-process (process)
8216fbaf
EZ
720 (delete-process process))
721
adf794e4 722(defvar rcirc-trap-errors-flag t)
bd43c990 723(defun rcirc-process-server-response (process text)
adf794e4 724 (if rcirc-trap-errors-flag
bd43c990
RS
725 (condition-case err
726 (rcirc-process-server-response-1 process text)
727 (error
728 (rcirc-print process "RCIRC" "ERROR" nil
adf794e4 729 (format "\"%s\" %s" text err) t)))
bd43c990
RS
730 (rcirc-process-server-response-1 process text)))
731
732(defun rcirc-process-server-response-1 (process text)
733 (if (string-match "^\\(:\\([^ ]+\\) \\)?\\([^ ]+\\) \\(.+\\)$" text)
db58efbf
EZ
734 (let* ((user (match-string 2 text))
735 (sender (rcirc-user-nick user))
bd43c990
RS
736 (cmd (match-string 3 text))
737 (args (match-string 4 text))
738 (handler (intern-soft (concat "rcirc-handler-" cmd))))
739 (string-match "^\\([^:]*\\):?\\(.+\\)?$" args)
740 (let* ((args1 (match-string 1 args))
741 (args2 (match-string 2 args))
adf794e4
EZ
742 (args (delq nil (append (split-string args1 " " t)
743 (list args2)))))
bd43c990
RS
744 (if (not (fboundp handler))
745 (rcirc-handler-generic process cmd sender args text)
746 (funcall handler process sender args text))
d1069532 747 (run-hook-with-args 'rcirc-receive-message-functions
bd43c990
RS
748 process cmd sender args text)))
749 (message "UNHANDLED: %s" text)))
750
f8db61b2
EZ
751(defvar rcirc-responses-no-activity '("305" "306")
752 "Responses that don't trigger activity in the mode-line indicator.")
753
754(defun rcirc-handler-generic (process response sender args text)
bd43c990 755 "Generic server response handler."
f8db61b2
EZ
756 (rcirc-print process sender response nil
757 (mapconcat 'identity (cdr args) " ")
758 (not (member response rcirc-responses-no-activity))))
bd43c990 759
488086f4
SM
760(defun rcirc--connection-open-p (process)
761 (memq (process-status process) '(run open)))
762
bd43c990
RS
763(defun rcirc-send-string (process string)
764 "Send PROCESS a STRING plus a newline."
a2524d26 765 (let ((string (concat (encode-coding-string string rcirc-encode-coding-system)
bd43c990 766 "\n")))
488086f4 767 (unless (rcirc--connection-open-p process)
53f831f3 768 (error "Network connection to %s is not open"
a2524d26 769 (process-name process)))
adf794e4 770 (rcirc-debug process string)
bd43c990
RS
771 (process-send-string process string)))
772
1be1d1e9
DD
773(defun rcirc-send-privmsg (process target string)
774 (rcirc-send-string process (format "PRIVMSG %s :%s" target string)))
775
776(defun rcirc-send-ctcp (process target request &optional args)
777 (let ((args (if args (concat " " args) "")))
778 (rcirc-send-privmsg process target
5708ce5e 779 (format "\C-a%s%s\C-a" request args))))
1be1d1e9 780
a2524d26
EZ
781(defun rcirc-buffer-process (&optional buffer)
782 "Return the process associated with channel BUFFER.
783With no argument or nil as argument, use the current buffer."
8216fbaf
EZ
784 (or (get-buffer-process (if buffer
785 (with-current-buffer buffer
786 rcirc-server-buffer)
787 rcirc-server-buffer))
788 rcirc-process))
a2524d26
EZ
789
790(defun rcirc-server-name (process)
791 "Return PROCESS server name, given by the 001 response."
adf794e4 792 (with-rcirc-process-buffer process
195eca78
SM
793 (or rcirc-server-name
794 (warn "server name for process %S unknown" process))))
bd43c990
RS
795
796(defun rcirc-nick (process)
797 "Return PROCESS nick."
92c4adc1 798 (with-rcirc-process-buffer process
a2524d26
EZ
799 (or rcirc-nick rcirc-default-nick)))
800
801(defun rcirc-buffer-nick (&optional buffer)
802 "Return the nick associated with BUFFER.
803With no argument or nil as argument, use the current buffer."
804 (with-current-buffer (or buffer (current-buffer))
805 (with-current-buffer rcirc-server-buffer
806 (or rcirc-nick rcirc-default-nick))))
bd43c990 807
02f47e86 808(defvar rcirc-max-message-length 420
bd43c990
RS
809 "Messages longer than this value will be split.")
810
4432d2e2
LL
811(defun rcirc-split-message (message)
812 "Split MESSAGE into chunks within `rcirc-max-message-length'."
27d6c5a8
LL
813 ;; `rcirc-encode-coding-system' can have buffer-local value.
814 (let ((encoding rcirc-encode-coding-system))
815 (with-temp-buffer
816 (insert message)
817 (goto-char (point-min))
818 (let (result)
819 (while (not (eobp))
820 (goto-char (or (byte-to-position rcirc-max-message-length)
821 (point-max)))
822 ;; max message length is 512 including CRLF
823 (while (and (not (bobp))
824 (> (length (encode-coding-region
825 (point-min) (point) encoding t))
826 rcirc-max-message-length))
827 (forward-char -1))
828 (push (delete-and-extract-region (point-min) (point)) result))
829 (nreverse result)))))
4432d2e2 830
195eca78 831(defun rcirc-send-message (process target message &optional noticep silent)
bd43c990 832 "Send TARGET associated with PROCESS a privmsg with text MESSAGE.
195eca78
SM
833If NOTICEP is non-nil, send a notice instead of privmsg.
834If SILENT is non-nil, do not print the message in any irc buffer."
4432d2e2 835 (let ((response (if noticep "NOTICE" "PRIVMSG")))
db58efbf 836 (rcirc-get-buffer-create process target)
4432d2e2
LL
837 (dolist (msg (rcirc-split-message message))
838 (rcirc-send-string process (concat response " " target " :" msg))
839 (unless silent
840 (rcirc-print process (rcirc-nick process) response target msg)))))
bd43c990
RS
841
842(defvar rcirc-input-ring nil)
843(defvar rcirc-input-ring-index 0)
27de4e20 844
bd43c990
RS
845(defun rcirc-prev-input-string (arg)
846 (ring-ref rcirc-input-ring (+ rcirc-input-ring-index arg)))
847
27de4e20
DD
848(defun rcirc-insert-prev-input ()
849 (interactive)
bd43c990
RS
850 (when (<= rcirc-prompt-end-marker (point))
851 (delete-region rcirc-prompt-end-marker (point-max))
852 (insert (rcirc-prev-input-string 0))
853 (setq rcirc-input-ring-index (1+ rcirc-input-ring-index))))
854
27de4e20
DD
855(defun rcirc-insert-next-input ()
856 (interactive)
bd43c990
RS
857 (when (<= rcirc-prompt-end-marker (point))
858 (delete-region rcirc-prompt-end-marker (point-max))
859 (setq rcirc-input-ring-index (1- rcirc-input-ring-index))
860 (insert (rcirc-prev-input-string -1))))
861
94c7243b
LL
862(defvar rcirc-server-commands
863 '("/admin" "/away" "/connect" "/die" "/error" "/info"
864 "/invite" "/ison" "/join" "/kick" "/kill" "/links"
865 "/list" "/lusers" "/mode" "/motd" "/names" "/nick"
866 "/notice" "/oper" "/part" "/pass" "/ping" "/pong"
867 "/privmsg" "/quit" "/rehash" "/restart" "/service" "/servlist"
868 "/server" "/squery" "/squit" "/stats" "/summon" "/time"
869 "/topic" "/trace" "/user" "/userhost" "/users" "/version"
870 "/wallops" "/who" "/whois" "/whowas")
871 "A list of user commands by IRC server.
872The value defaults to RFCs 1459 and 2812.")
873
874;; /me and /ctcp are not defined by `defun-rcirc-command'.
875(defvar rcirc-client-commands '("/me" "/ctcp")
876 "A list of user commands defined by IRC client rcirc.
877The list is updated automatically by `defun-rcirc-command'.")
878
879(defun rcirc-completion-at-point ()
880 "Function used for `completion-at-point-functions' in `rcirc-mode'."
0b4e93f1
LL
881 (and (rcirc-looking-at-input)
882 (let* ((beg (save-excursion
883 (if (re-search-backward " " rcirc-prompt-end-marker t)
884 (1+ (point))
885 rcirc-prompt-end-marker)))
886 (table (if (and (= beg rcirc-prompt-end-marker)
887 (eq (char-after beg) ?/))
888 (delete-dups
889 (nconc (sort (copy-sequence rcirc-client-commands)
890 'string-lessp)
891 (sort (copy-sequence rcirc-server-commands)
892 'string-lessp)))
893 (rcirc-channel-nicks (rcirc-buffer-process)
894 rcirc-target))))
895 (list beg (point) table))))
94c7243b
LL
896
897(defvar rcirc-completions nil)
898(defvar rcirc-completion-start nil)
899
900(defun rcirc-complete ()
901 "Cycle through completions from list of nicks in channel or IRC commands.
902IRC command completion is performed only if '/' is the first input char."
bd43c990 903 (interactive)
0b4e93f1
LL
904 (unless (rcirc-looking-at-input)
905 (error "Point not located after rcirc prompt"))
7faa3f8c 906 (if (eq last-command this-command)
94c7243b
LL
907 (setq rcirc-completions
908 (append (cdr rcirc-completions) (list (car rcirc-completions))))
909 (let ((completion-ignore-case t)
910 (table (rcirc-completion-at-point)))
911 (setq rcirc-completion-start (car table))
912 (setq rcirc-completions
0b4e93f1
LL
913 (and rcirc-completion-start
914 (all-completions (buffer-substring rcirc-completion-start
915 (cadr table))
916 (nth 2 table))))))
94c7243b 917 (let ((completion (car rcirc-completions)))
bd43c990 918 (when completion
94c7243b
LL
919 (delete-region rcirc-completion-start (point))
920 (insert
2a4466ca
DD
921 (cond
922 ((= (aref completion 0) ?/) (concat completion " "))
923 ((= rcirc-completion-start rcirc-prompt-end-marker)
924 (format rcirc-nick-completion-format completion))
925 (t completion))))))
bd43c990 926
a2524d26
EZ
927(defun set-rcirc-decode-coding-system (coding-system)
928 "Set the decode coding system used in this channel."
929 (interactive "zCoding system for incoming messages: ")
488086f4 930 (set (make-local-variable 'rcirc-decode-coding-system) coding-system))
a2524d26
EZ
931
932(defun set-rcirc-encode-coding-system (coding-system)
933 "Set the encode coding system used in this channel."
934 (interactive "zCoding system for outgoing messages: ")
488086f4 935 (set (make-local-variable 'rcirc-encode-coding-system) coding-system))
bd43c990 936
b016851c
SM
937(defvar rcirc-mode-map
938 (let ((map (make-sparse-keymap)))
939 (define-key map (kbd "RET") 'rcirc-send-input)
940 (define-key map (kbd "M-p") 'rcirc-insert-prev-input)
941 (define-key map (kbd "M-n") 'rcirc-insert-next-input)
942 (define-key map (kbd "TAB") 'rcirc-complete)
943 (define-key map (kbd "C-c C-b") 'rcirc-browse-url)
944 (define-key map (kbd "C-c C-c") 'rcirc-edit-multiline)
945 (define-key map (kbd "C-c C-j") 'rcirc-cmd-join)
946 (define-key map (kbd "C-c C-k") 'rcirc-cmd-kick)
947 (define-key map (kbd "C-c C-l") 'rcirc-toggle-low-priority)
948 (define-key map (kbd "C-c C-d") 'rcirc-cmd-mode)
949 (define-key map (kbd "C-c C-m") 'rcirc-cmd-msg)
950 (define-key map (kbd "C-c C-r") 'rcirc-cmd-nick) ; rename
951 (define-key map (kbd "C-c C-o") 'rcirc-omit-mode)
b016851c
SM
952 (define-key map (kbd "C-c C-p") 'rcirc-cmd-part)
953 (define-key map (kbd "C-c C-q") 'rcirc-cmd-query)
954 (define-key map (kbd "C-c C-t") 'rcirc-cmd-topic)
955 (define-key map (kbd "C-c C-n") 'rcirc-cmd-names)
956 (define-key map (kbd "C-c C-w") 'rcirc-cmd-whois)
957 (define-key map (kbd "C-c C-x") 'rcirc-cmd-quit)
958 (define-key map (kbd "C-c TAB") ; C-i
959 'rcirc-toggle-ignore-buffer-activity)
960 (define-key map (kbd "C-c C-s") 'rcirc-switch-to-server-buffer)
961 (define-key map (kbd "C-c C-a") 'rcirc-jump-to-first-unread-line)
962 map)
bd43c990
RS
963 "Keymap for rcirc mode.")
964
adf794e4
EZ
965(defvar rcirc-short-buffer-name nil
966 "Generated abbreviation to use to indicate buffer activity.")
967
bd43c990
RS
968(defvar rcirc-mode-hook nil
969 "Hook run when setting up rcirc buffer.")
970
a2524d26
EZ
971(defvar rcirc-last-post-time nil)
972
195eca78
SM
973(defvar rcirc-log-alist nil
974 "Alist of lines to log to disk when `rcirc-log-flag' is non-nil.
975Each element looks like (FILENAME . TEXT).")
976
a0a5c583
GM
977(defvar rcirc-current-line 0
978 "The current number of responses printed in this channel.
979This number is independent of the number of lines in the buffer.")
980
bd43c990 981(defun rcirc-mode (process target)
4d789d84 982 ;; FIXME: Use define-derived-mode.
2e398771 983 "Major mode for IRC channel buffers.
bd43c990
RS
984
985\\{rcirc-mode-map}"
986 (kill-all-local-variables)
987 (use-local-map rcirc-mode-map)
988 (setq mode-name "rcirc")
989 (setq major-mode 'rcirc-mode)
195eca78 990 (setq mode-line-process nil)
bd43c990 991
488086f4 992 (set (make-local-variable 'rcirc-input-ring)
183fc730
LL
993 ;; If rcirc-input-ring is already a ring with desired size do
994 ;; not re-initialize.
995 (if (and (ring-p rcirc-input-ring)
996 (= (ring-size rcirc-input-ring)
997 rcirc-input-ring-size))
998 rcirc-input-ring
999 (make-ring rcirc-input-ring-size)))
488086f4
SM
1000 (set (make-local-variable 'rcirc-server-buffer) (process-buffer process))
1001 (set (make-local-variable 'rcirc-target) target)
1002 (set (make-local-variable 'rcirc-topic) nil)
1003 (set (make-local-variable 'rcirc-last-post-time) (current-time))
1004 (set (make-local-variable 'fill-paragraph-function) 'rcirc-fill-paragraph)
1005 (set (make-local-variable 'rcirc-recent-quit-alist) nil)
1006 (set (make-local-variable 'rcirc-current-line) 0)
1007
1a2ce9ee 1008 (use-hard-newlines t)
488086f4 1009 (set (make-local-variable 'rcirc-short-buffer-name) nil)
82745640 1010 (set (make-local-variable 'rcirc-urls) nil)
bd43c990 1011
a0a5c583
GM
1012 ;; setup for omitting responses
1013 (setq buffer-invisibility-spec '())
1014 (setq buffer-display-table (make-display-table))
1015 (set-display-table-slot buffer-display-table 4
aebf69c8 1016 (let ((glyph (make-glyph-code
a0a5c583
GM
1017 ?. 'font-lock-keyword-face)))
1018 (make-vector 3 glyph)))
1019
a2524d26
EZ
1020 (dolist (i rcirc-coding-system-alist)
1021 (let ((chan (if (consp (car i)) (caar i) (car i)))
1022 (serv (if (consp (car i)) (cdar i) "")))
1023 (when (and (string-match chan (or target ""))
1024 (string-match serv (rcirc-server-name process)))
488086f4
SM
1025 (set (make-local-variable 'rcirc-decode-coding-system)
1026 (if (consp (cdr i)) (cadr i) (cdr i)))
1027 (set (make-local-variable 'rcirc-encode-coding-system)
1028 (if (consp (cdr i)) (cddr i) (cdr i))))))
a2524d26 1029
bd43c990 1030 ;; setup the prompt and markers
488086f4
SM
1031 (set (make-local-variable 'rcirc-prompt-start-marker) (point-max-marker))
1032 (set (make-local-variable 'rcirc-prompt-end-marker) (point-max-marker))
bd43c990
RS
1033 (rcirc-update-prompt)
1034 (goto-char rcirc-prompt-end-marker)
488086f4
SM
1035
1036 (set (make-local-variable 'overlay-arrow-position) (make-marker))
bd43c990 1037
a2524d26
EZ
1038 ;; if the user changes the major mode or kills the buffer, there is
1039 ;; cleanup work to do
f8db61b2
EZ
1040 (add-hook 'change-major-mode-hook 'rcirc-change-major-mode-hook nil t)
1041 (add-hook 'kill-buffer-hook 'rcirc-kill-buffer-hook nil t)
a2524d26 1042
adf794e4
EZ
1043 ;; add to buffer list, and update buffer abbrevs
1044 (when target ; skip server buffer
1045 (let ((buffer (current-buffer)))
1046 (with-rcirc-process-buffer process
1047 (setq rcirc-buffer-alist (cons (cons target buffer)
1048 rcirc-buffer-alist))))
1049 (rcirc-update-short-buffer-names))
1050
94c7243b
LL
1051 (add-hook 'completion-at-point-functions
1052 'rcirc-completion-at-point nil 'local)
1053
4d789d84 1054 (run-mode-hooks 'rcirc-mode-hook))
bd43c990 1055
adf794e4
EZ
1056(defun rcirc-update-prompt (&optional all)
1057 "Reset the prompt string in the current buffer.
c18a54de 1058
adf794e4
EZ
1059If ALL is non-nil, update prompts in all IRC buffers."
1060 (if all
1061 (mapc (lambda (process)
1062 (mapc (lambda (buffer)
1063 (with-current-buffer buffer
1064 (rcirc-update-prompt)))
1065 (with-rcirc-process-buffer process
1066 (mapcar 'cdr rcirc-buffer-alist))))
1067 (rcirc-process-list))
1068 (let ((inhibit-read-only t)
1069 (prompt (or rcirc-prompt "")))
1070 (mapc (lambda (rep)
1071 (setq prompt
a2524d26
EZ
1072 (replace-regexp-in-string (car rep) (cdr rep) prompt)))
1073 (list (cons "%n" (rcirc-buffer-nick))
8216fbaf 1074 (cons "%s" (with-rcirc-server-buffer rcirc-server-name))
adf794e4
EZ
1075 (cons "%t" (or rcirc-target ""))))
1076 (save-excursion
1077 (delete-region rcirc-prompt-start-marker rcirc-prompt-end-marker)
1078 (goto-char rcirc-prompt-start-marker)
1079 (let ((start (point)))
1080 (insert-before-markers prompt)
1081 (set-marker rcirc-prompt-start-marker start)
1082 (when (not (zerop (- rcirc-prompt-end-marker
1083 rcirc-prompt-start-marker)))
1084 (add-text-properties rcirc-prompt-start-marker
1085 rcirc-prompt-end-marker
1086 (list 'face 'rcirc-prompt
1087 'read-only t 'field t
1088 'front-sticky t 'rear-nonsticky t))))))))
1089
1090(defun rcirc-set-changed (option value)
1091 "Set OPTION to VALUE and do updates after a customization change."
1092 (set-default option value)
1093 (cond ((eq option 'rcirc-prompt)
1094 (rcirc-update-prompt 'all))
1095 (t
1096 (error "Bad option %s" option))))
bd43c990
RS
1097
1098(defun rcirc-channel-p (target)
1099 "Return t if TARGET is a channel name."
1100 (and target
1101 (not (zerop (length target)))
1102 (or (eq (aref target 0) ?#)
1103 (eq (aref target 0) ?&))))
1104
2d7d6439
GM
1105(defcustom rcirc-log-directory "~/.emacs.d/rcirc-log"
1106 "Directory to keep IRC logfiles."
1107 :type 'directory
1108 :group 'rcirc)
1109
1110(defcustom rcirc-log-flag nil
1111 "Non-nil means log IRC activity to disk.
1112Logfiles are kept in `rcirc-log-directory'."
1113 :type 'boolean
1114 :group 'rcirc)
1115
bd43c990 1116(defun rcirc-kill-buffer-hook ()
a63067fc
DD
1117 "Part the channel when killing an rcirc buffer.
1118
1119If `rcirc-kill-channel-buffers' is non-nil and the killed buffer
1120is a server buffer, kills all of the channel buffers associated
1121with it."
bd43c990 1122 (when (eq major-mode 'rcirc-mode)
80094035
GK
1123 (when (and rcirc-log-flag
1124 rcirc-log-directory)
1125 (rcirc-log-write))
a63067fc
DD
1126 (rcirc-clean-up-buffer "Killed buffer")
1127 (when (and rcirc-buffer-alist ;; it's a server buffer
1128 rcirc-kill-channel-buffers)
1129 (dolist (channel rcirc-buffer-alist)
1130 (kill-buffer (cdr channel))))))
a2524d26
EZ
1131
1132(defun rcirc-change-major-mode-hook ()
1133 "Part the channel when changing the major-mode."
1134 (rcirc-clean-up-buffer "Changed major mode"))
1135
1136(defun rcirc-clean-up-buffer (reason)
adf794e4
EZ
1137 (let ((buffer (current-buffer)))
1138 (rcirc-clear-activity buffer)
a2524d26 1139 (when (and (rcirc-buffer-process)
488086f4 1140 (rcirc--connection-open-p (rcirc-buffer-process)))
a2524d26
EZ
1141 (with-rcirc-server-buffer
1142 (setq rcirc-buffer-alist
1143 (rassq-delete-all buffer rcirc-buffer-alist)))
adf794e4 1144 (rcirc-update-short-buffer-names)
bd43c990 1145 (if (rcirc-channel-p rcirc-target)
a2524d26
EZ
1146 (rcirc-send-string (rcirc-buffer-process)
1147 (concat "PART " rcirc-target " :" reason))
adf794e4 1148 (when rcirc-target
a2524d26
EZ
1149 (rcirc-remove-nick-channel (rcirc-buffer-process)
1150 (rcirc-buffer-nick)
195eca78
SM
1151 rcirc-target))))
1152 (setq rcirc-target nil)))
adf794e4 1153
adf794e4
EZ
1154(defun rcirc-generate-new-buffer-name (process target)
1155 "Return a buffer name based on PROCESS and TARGET.
2e398771 1156This is used for the initial name given to IRC buffers."
195eca78
SM
1157 (substring-no-properties
1158 (if target
1159 (concat target "@" (process-name process))
1160 (concat "*" (process-name process) "*"))))
bd43c990 1161
adf794e4 1162(defun rcirc-get-buffer (process target &optional server)
bd43c990 1163 "Return the buffer associated with the PROCESS and TARGET.
adf794e4 1164
adf794e4
EZ
1165If optional argument SERVER is non-nil, return the server buffer
1166if there is no existing buffer for TARGET, otherwise return nil."
1167 (with-rcirc-process-buffer process
1168 (if (null target)
1169 (current-buffer)
1170 (let ((buffer (cdr (assoc-string target rcirc-buffer-alist t))))
1171 (or buffer (when server (current-buffer)))))))
bd43c990
RS
1172
1173(defun rcirc-get-buffer-create (process target)
adf794e4
EZ
1174 "Return the buffer associated with the PROCESS and TARGET.
1175Create the buffer if it doesn't exist."
1176 (let ((buffer (rcirc-get-buffer process target)))
a2524d26 1177 (if (and buffer (buffer-live-p buffer))
2fbed782 1178 (with-current-buffer buffer
db58efbf 1179 (when (not rcirc-target)
2fbed782 1180 (setq rcirc-target target))
db58efbf 1181 buffer)
195eca78
SM
1182 ;; create the buffer
1183 (with-rcirc-process-buffer process
1184 (let ((new-buffer (get-buffer-create
1185 (rcirc-generate-new-buffer-name process target))))
1186 (with-current-buffer new-buffer
a0a5c583 1187 (rcirc-mode process target)
aebf69c8 1188 (rcirc-put-nick-channel process (rcirc-nick process) target
a0a5c583 1189 rcirc-current-line))
195eca78 1190 new-buffer)))))
bd43c990
RS
1191
1192(defun rcirc-send-input ()
1193 "Send input to target associated with the current buffer."
1194 (interactive)
53f831f3
AS
1195 (if (< (point) rcirc-prompt-end-marker)
1196 ;; copy the line down to the input area
1197 (progn
1198 (forward-line 0)
1199 (let ((start (if (eq (point) (point-min))
1200 (point)
1201 (if (get-text-property (1- (point)) 'hard)
1202 (point)
1203 (previous-single-property-change (point) 'hard))))
1204 (end (next-single-property-change (1+ (point)) 'hard)))
1205 (goto-char (point-max))
1206 (insert (replace-regexp-in-string
1207 "\n\\s-+" " "
1208 (buffer-substring-no-properties start end)))))
1209 ;; process input
1210 (goto-char (point-max))
a2524d26
EZ
1211 (when (not (equal 0 (- (point) rcirc-prompt-end-marker)))
1212 ;; delete a trailing newline
1213 (when (eq (point) (point-at-bol))
d355a0b7 1214 (delete-char -1))
a2524d26
EZ
1215 (let ((input (buffer-substring-no-properties
1216 rcirc-prompt-end-marker (point))))
1217 (dolist (line (split-string input "\n"))
1218 (rcirc-process-input-line line))
1219 ;; add to input-ring
1220 (save-excursion
1221 (ring-insert rcirc-input-ring input)
1222 (setq rcirc-input-ring-index 0))))))
1223
195eca78
SM
1224(defun rcirc-fill-paragraph (&optional arg)
1225 (interactive "p")
1226 (when (> (point) rcirc-prompt-end-marker)
1227 (save-restriction
1228 (narrow-to-region rcirc-prompt-end-marker (point-max))
1229 (let ((fill-column rcirc-max-message-length))
1230 (fill-region (point-min) (point-max))))))
1231
a2524d26 1232(defun rcirc-process-input-line (line)
db58efbf
EZ
1233 (if (string-match "^/\\([^ ]+\\) ?\\(.*\\)$" line)
1234 (rcirc-process-command (match-string 1 line)
1235 (match-string 2 line)
1236 line)
1237 (rcirc-process-message line)))
1238
1239(defun rcirc-process-message (line)
1240 (if (not rcirc-target)
a2524d26 1241 (message "Not joined (no target)")
db58efbf 1242 (delete-region rcirc-prompt-end-marker (point))
a2524d26
EZ
1243 (rcirc-send-message (rcirc-buffer-process) rcirc-target line)
1244 (setq rcirc-last-post-time (current-time))))
db58efbf
EZ
1245
1246(defun rcirc-process-command (command args line)
1247 (if (eq (aref command 0) ?/)
1248 ;; "//text" will send "/text" as a message
1249 (rcirc-process-message (substring line 1))
a2524d26
EZ
1250 (let ((fun (intern-soft (concat "rcirc-cmd-" command)))
1251 (process (rcirc-buffer-process)))
db58efbf
EZ
1252 (newline)
1253 (with-current-buffer (current-buffer)
1254 (delete-region rcirc-prompt-end-marker (point))
1255 (if (string= command "me")
a2524d26 1256 (rcirc-print process (rcirc-buffer-nick)
db58efbf 1257 "ACTION" rcirc-target args)
a2524d26 1258 (rcirc-print process (rcirc-buffer-nick)
db58efbf
EZ
1259 "COMMAND" rcirc-target line))
1260 (set-marker rcirc-prompt-end-marker (point))
1261 (if (fboundp fun)
a2524d26
EZ
1262 (funcall fun args process rcirc-target)
1263 (rcirc-send-string process
f8db61b2 1264 (concat command " :" args)))))))
db58efbf 1265
bd43c990 1266(defvar rcirc-parent-buffer nil)
488086f4
SM
1267(make-variable-buffer-local 'rcirc-parent-buffer)
1268(put 'rcirc-parent-buffer 'permanent-local t)
bd43c990
RS
1269(defvar rcirc-window-configuration nil)
1270(defun rcirc-edit-multiline ()
1271 "Move current edit to a dedicated buffer."
1272 (interactive)
1273 (let ((pos (1+ (- (point) rcirc-prompt-end-marker))))
1274 (goto-char (point-max))
aebf69c8 1275 (let ((text (buffer-substring-no-properties rcirc-prompt-end-marker
a0a5c583 1276 (point)))
a2524d26 1277 (parent (buffer-name)))
bd43c990
RS
1278 (delete-region rcirc-prompt-end-marker (point))
1279 (setq rcirc-window-configuration (current-window-configuration))
1280 (pop-to-buffer (concat "*multiline " parent "*"))
a2524d26
EZ
1281 (funcall rcirc-multiline-major-mode)
1282 (rcirc-multiline-minor-mode 1)
bd43c990 1283 (setq rcirc-parent-buffer parent)
bd43c990 1284 (insert text)
db58efbf
EZ
1285 (and (> pos 0) (goto-char pos))
1286 (message "Type C-c C-c to return text to %s, or C-c C-k to cancel" parent))))
bd43c990 1287
b016851c
SM
1288(defvar rcirc-multiline-minor-mode-map
1289 (let ((map (make-sparse-keymap)))
1290 (define-key map (kbd "C-c C-c") 'rcirc-multiline-minor-submit)
1291 (define-key map (kbd "C-x C-s") 'rcirc-multiline-minor-submit)
1292 (define-key map (kbd "C-c C-k") 'rcirc-multiline-minor-cancel)
1293 (define-key map (kbd "ESC ESC ESC") 'rcirc-multiline-minor-cancel)
1294 map)
a2524d26 1295 "Keymap for multiline mode in rcirc.")
a2524d26
EZ
1296
1297(define-minor-mode rcirc-multiline-minor-mode
e1ac4066
GM
1298 "Minor mode for editing multiple lines in rcirc.
1299With a prefix argument ARG, enable the mode if ARG is positive,
1300and disable it otherwise. If called from Lisp, enable the mode
1301if ARG is omitted or nil."
a2524d26
EZ
1302 :init-value nil
1303 :lighter " rcirc-mline"
1304 :keymap rcirc-multiline-minor-mode-map
1305 :global nil
1306 :group 'rcirc
02f47e86 1307 (setq fill-column rcirc-max-message-length))
a2524d26
EZ
1308
1309(defun rcirc-multiline-minor-submit ()
bd43c990
RS
1310 "Send the text in buffer back to parent buffer."
1311 (interactive)
adf794e4 1312 (untabify (point-min) (point-max))
bd43c990
RS
1313 (let ((text (buffer-substring (point-min) (point-max)))
1314 (buffer (current-buffer))
1315 (pos (point)))
1316 (set-buffer rcirc-parent-buffer)
1317 (goto-char (point-max))
1318 (insert text)
bd43c990 1319 (kill-buffer buffer)
adf794e4
EZ
1320 (set-window-configuration rcirc-window-configuration)
1321 (goto-char (+ rcirc-prompt-end-marker (1- pos)))))
bd43c990 1322
a2524d26 1323(defun rcirc-multiline-minor-cancel ()
bd43c990
RS
1324 "Cancel the multiline edit."
1325 (interactive)
bd43c990
RS
1326 (kill-buffer (current-buffer))
1327 (set-window-configuration rcirc-window-configuration))
1328
2fbed782 1329(defun rcirc-any-buffer (process)
adf794e4 1330 "Return a buffer for PROCESS, either the one selected or the process buffer."
2fbed782
EZ
1331 (if rcirc-always-use-server-buffer-flag
1332 (process-buffer process)
1333 (let ((buffer (window-buffer (selected-window))))
1334 (if (and buffer
1335 (with-current-buffer buffer
1336 (and (eq major-mode 'rcirc-mode)
a2524d26 1337 (eq (rcirc-buffer-process) process))))
2fbed782
EZ
1338 buffer
1339 (process-buffer process)))))
bd43c990 1340
324e4da7 1341(defcustom rcirc-response-formats
195eca78
SM
1342 '(("PRIVMSG" . "<%N> %m")
1343 ("NOTICE" . "-%N- %m")
1344 ("ACTION" . "[%N %m]")
1345 ("COMMAND" . "%m")
1346 ("ERROR" . "%fw!!! %m")
1347 (t . "%fp*** %fs%n %r %m"))
324e4da7
MB
1348 "An alist of formats used for printing responses.
1349The format is looked up using the response-type as a key;
1350if no match is found, the default entry (with a key of `t') is used.
1351
1352The entry's value part should be a string, which is inserted with
1353the of the following escape sequences replaced by the described values:
1354
1355 %m The message text
2fbed782
EZ
1356 %n The sender's nick
1357 %N The sender's nick (with face `rcirc-my-nick' or `rcirc-other-nick')
324e4da7 1358 %r The response-type
324e4da7
MB
1359 %t The target
1360 %fw Following text uses the face `font-lock-warning-face'
1361 %fp Following text uses the face `rcirc-server-prefix'
1362 %fs Following text uses the face `rcirc-server'
1363 %f[FACE] Following text uses the face FACE
3715419e 1364 %f- Following text uses the default face
a2524d26 1365 %% A literal `%' character"
324e4da7
MB
1366 :type '(alist :key-type (choice (string :tag "Type")
1367 (const :tag "Default" t))
1368 :value-type string)
1369 :group 'rcirc)
1370
0ffab1eb 1371(defcustom rcirc-omit-responses
a0a5c583 1372 '("JOIN" "PART" "QUIT" "NICK")
195eca78
SM
1373 "Responses which will be hidden when `rcirc-omit-mode' is enabled."
1374 :type '(repeat string)
1375 :group 'rcirc)
1376
bd43c990 1377(defun rcirc-format-response-string (process sender response target text)
324e4da7
MB
1378 "Return a nicely-formatted response string, incorporating TEXT
1379\(and perhaps other arguments). The specific formatting used
1380is found by looking up RESPONSE in `rcirc-response-formats'."
195eca78
SM
1381 (with-temp-buffer
1382 (insert (or (cdr (assoc response rcirc-response-formats))
1383 (cdr (assq t rcirc-response-formats))))
1384 (goto-char (point-min))
1385 (let ((start (point-min))
1386 (sender (if (or (not sender)
1387 (string= (rcirc-server-name process) sender))
1388 ""
1389 sender))
1390 face)
1391 (while (re-search-forward "%\\(\\(f\\(.\\)\\)\\|\\(.\\)\\)" nil t)
1392 (rcirc-add-face start (match-beginning 0) face)
1393 (setq start (match-beginning 0))
1394 (replace-match
1395 (case (aref (match-string 1) 0)
1396 (?f (setq face
1397 (case (string-to-char (match-string 3))
1398 (?w 'font-lock-warning-face)
1399 (?p 'rcirc-server-prefix)
1400 (?s 'rcirc-server)
1401 (t nil)))
1402 "")
1403 (?n sender)
1404 (?N (let ((my-nick (rcirc-nick process)))
1405 (save-match-data
1406 (with-syntax-table rcirc-nick-syntax-table
1407 (rcirc-facify sender
1408 (cond ((string= sender my-nick)
1409 'rcirc-my-nick)
1410 ((and rcirc-bright-nicks
0ffab1eb 1411 (string-match
195eca78
SM
1412 (regexp-opt rcirc-bright-nicks
1413 'words)
1414 sender))
1415 'rcirc-bright-nick)
1416 ((and rcirc-dim-nicks
1417 (string-match
1418 (regexp-opt rcirc-dim-nicks
1419 'words)
1420 sender))
1421 'rcirc-dim-nick)
1422 (t
1423 'rcirc-other-nick)))))))
1424 (?m (propertize text 'rcirc-text text))
1425 (?r response)
1426 (?t (or target ""))
1427 (t (concat "UNKNOWN CODE:" (match-string 0))))
1428 t t nil 0)
1429 (rcirc-add-face (match-beginning 0) (match-end 0) face))
1430 (rcirc-add-face start (match-beginning 0) face))
1431 (buffer-substring (point-min) (point-max))))
bd43c990 1432
db58efbf
EZ
1433(defun rcirc-target-buffer (process sender response target text)
1434 "Return a buffer to print the server response."
1435 (assert (not (bufferp target)))
1436 (with-rcirc-process-buffer process
1437 (cond ((not target)
2fbed782 1438 (rcirc-any-buffer process))
db58efbf
EZ
1439 ((not (rcirc-channel-p target))
1440 ;; message from another user
195eca78
SM
1441 (if (or (string= response "PRIVMSG")
1442 (string= response "ACTION"))
db58efbf
EZ
1443 (rcirc-get-buffer-create process (if (string= sender rcirc-nick)
1444 target
1445 sender))
1446 (rcirc-get-buffer process target t)))
1447 ((or (rcirc-get-buffer process target)
2fbed782 1448 (rcirc-any-buffer process))))))
db58efbf 1449
f8db61b2
EZ
1450(defvar rcirc-activity-types nil)
1451(make-variable-buffer-local 'rcirc-activity-types)
a2524d26
EZ
1452(defvar rcirc-last-sender nil)
1453(make-variable-buffer-local 'rcirc-last-sender)
7faa3f8c 1454
a0a5c583
GM
1455(defcustom rcirc-omit-threshold 100
1456 "Number of lines since last activity from a nick before `rcirc-omit-responses' are omitted."
1457 :type 'integer
1458 :group 'rcirc)
1459
8f10c937
DD
1460(defcustom rcirc-log-process-buffers nil
1461 "Non-nil if rcirc process buffers should be logged to disk."
1462 :group 'rcirc
1463 :type 'boolean
1464 :version "24.1")
1465
683b7dc6 1466(defun rcirc-last-quit-line (process nick target)
a0a5c583
GM
1467 "Return the line number where NICK left TARGET.
1468Returns nil if the information is not recorded."
683b7dc6 1469 (let ((chanbuf (rcirc-get-buffer process target)))
a0a5c583
GM
1470 (when chanbuf
1471 (cdr (assoc-string nick (with-current-buffer chanbuf
1472 rcirc-recent-quit-alist))))))
1473
683b7dc6 1474(defun rcirc-last-line (process nick target)
a0a5c583 1475 "Return the line from the last activity from NICK in TARGET."
683b7dc6 1476 (let* ((chanbuf (rcirc-get-buffer process target))
a0a5c583
GM
1477 (line (or (cdr (assoc-string target
1478 (gethash nick (with-rcirc-server-buffer
1479 rcirc-nick-table)) t))
683b7dc6 1480 (rcirc-last-quit-line process nick target))))
a0a5c583
GM
1481 (if line
1482 line
1483 ;;(message "line is nil for %s in %s" nick target)
1484 nil)))
1485
683b7dc6 1486(defun rcirc-elapsed-lines (process nick target)
a0a5c583 1487 "Return the number of lines since activity from NICK in TARGET."
683b7dc6 1488 (let ((last-activity-line (rcirc-last-line process nick target)))
a0a5c583
GM
1489 (when (and last-activity-line
1490 (> last-activity-line 0))
1491 (- rcirc-current-line last-activity-line))))
1492
729f1525
DN
1493(defvar rcirc-markup-text-functions
1494 '(rcirc-markup-attributes
1495 rcirc-markup-my-nick
1496 rcirc-markup-urls
1497 rcirc-markup-keywords
683b7dc6 1498 rcirc-markup-bright-nicks)
729f1525
DN
1499
1500 "List of functions used to manipulate text before it is printed.
1501
683b7dc6
GM
1502Each function takes two arguments, SENDER, and RESPONSE. The
1503buffer is narrowed with the text to be printed and the point is
1504at the beginning of the `rcirc-text' propertized text.")
729f1525 1505
bd43c990
RS
1506(defun rcirc-print (process sender response target text &optional activity)
1507 "Print TEXT in the buffer associated with TARGET.
1508Format based on SENDER and RESPONSE. If ACTIVITY is non-nil,
1509record activity."
a2524d26 1510 (or text (setq text ""))
0ffab1eb
TTN
1511 (unless (and (or (member sender rcirc-ignore-list)
1512 (member (with-syntax-table rcirc-nick-syntax-table
1513 (when (string-match "^\\([^/]\\w*\\)[:,]" text)
1514 (match-string 1 text)))
1515 rcirc-ignore-list))
a0a5c583 1516 ;; do not ignore if we sent the message
aebf69c8 1517 (not (string= sender (rcirc-nick process))))
db58efbf 1518 (let* ((buffer (rcirc-target-buffer process sender response target text))
2c8abe90
AS
1519 (inhibit-read-only t))
1520 (with-current-buffer buffer
1521 (let ((moving (= (point) rcirc-prompt-end-marker))
1522 (old-point (point-marker))
1523 (fill-start (marker-position rcirc-prompt-start-marker)))
1524
108bf785 1525 (setq text (decode-coding-string text rcirc-decode-coding-system))
2c8abe90 1526 (unless (string= sender (rcirc-nick process))
2c8abe90
AS
1527 ;; mark the line with overlay arrow
1528 (unless (or (marker-position overlay-arrow-position)
195eca78
SM
1529 (get-buffer-window (current-buffer))
1530 (member response rcirc-omit-responses))
2c8abe90
AS
1531 (set-marker overlay-arrow-position
1532 (marker-position rcirc-prompt-start-marker))))
1533
1534 ;; temporarily set the marker insertion-type because
1535 ;; insert-before-markers results in hidden text in new buffers
1536 (goto-char rcirc-prompt-start-marker)
1537 (set-marker-insertion-type rcirc-prompt-start-marker t)
1538 (set-marker-insertion-type rcirc-prompt-end-marker t)
324e4da7 1539
195eca78 1540 (let ((start (point)))
0ffab1eb 1541 (insert (rcirc-format-response-string process sender response nil
195eca78
SM
1542 text)
1543 (propertize "\n" 'hard t))
1544
1545 ;; squeeze spaces out of text before rcirc-text
0ffab1eb 1546 (fill-region fill-start
195eca78
SM
1547 (1- (or (next-single-property-change fill-start
1548 'rcirc-text)
1549 rcirc-prompt-end-marker)))
1550
1551 ;; run markup functions
1552 (save-excursion
1553 (save-restriction
1554 (narrow-to-region start rcirc-prompt-start-marker)
1555 (goto-char (or (next-single-property-change start 'rcirc-text)
1556 (point)))
1557 (when (rcirc-buffer-process)
1558 (save-excursion (rcirc-markup-timestamp sender response))
1559 (dolist (fn rcirc-markup-text-functions)
1560 (save-excursion (funcall fn sender response)))
a0a5c583
GM
1561 (when rcirc-fill-flag
1562 (save-excursion (rcirc-markup-fill sender response))))
195eca78
SM
1563
1564 (when rcirc-read-only-flag
1565 (add-text-properties (point-min) (point-max)
1566 '(read-only t front-sticky t))))
1567 ;; make text omittable
683b7dc6
GM
1568 (let ((last-activity-lines (rcirc-elapsed-lines process sender target)))
1569 (if (and (not (string= (rcirc-nick process) sender))
1570 (member response rcirc-omit-responses)
1571 (or (not last-activity-lines)
1572 (< rcirc-omit-threshold last-activity-lines)))
1573 (put-text-property (1- start) (1- rcirc-prompt-start-marker)
1574 'invisible 'rcirc-omit)
1575 ;; otherwise increment the line count
1576 (setq rcirc-current-line (1+ rcirc-current-line))))))
195eca78
SM
1577
1578 (set-marker-insertion-type rcirc-prompt-start-marker nil)
1579 (set-marker-insertion-type rcirc-prompt-end-marker nil)
2c8abe90
AS
1580
1581 ;; truncate buffer if it is very long
1582 (save-excursion
1583 (when (and rcirc-buffer-maximum-lines
1584 (> rcirc-buffer-maximum-lines 0)
1585 (= (forward-line (- rcirc-buffer-maximum-lines)) 0))
1586 (delete-region (point-min) (point))))
1587
1588 ;; set the window point for buffers show in windows
1589 (walk-windows (lambda (w)
d40ac716
CY
1590 (when (and (not (eq (selected-window) w))
1591 (eq (current-buffer)
1592 (window-buffer w))
1593 (>= (window-point w)
1594 rcirc-prompt-end-marker))
195eca78 1595 (set-window-point w (point-max))))
2c8abe90
AS
1596 nil t)
1597
1598 ;; restore the point
1599 (goto-char (if moving rcirc-prompt-end-marker old-point))
1600
195eca78 1601 ;; keep window on bottom line if it was already there
d40ac716 1602 (when rcirc-scroll-show-maximum-output
d37e5c87
DD
1603 (let ((window (get-buffer-window)))
1604 (when window
1605 (with-selected-window window
1606 (when (eq major-mode 'rcirc-mode)
1607 (when (<= (- (window-height)
1608 (count-screen-lines (window-point)
1609 (window-start))
1610 1)
1611 0)
1612 (recenter -1)))))))
d40ac716 1613
2c8abe90
AS
1614 ;; flush undo (can we do something smarter here?)
1615 (buffer-disable-undo)
1616 (buffer-enable-undo))
1617
37269466 1618 ;; record mode line activity
f8db61b2
EZ
1619 (when (and activity
1620 (not rcirc-ignore-buffer-activity-flag)
1621 (not (and rcirc-dim-nicks sender
195eca78
SM
1622 (string-match (regexp-opt rcirc-dim-nicks) sender)
1623 (rcirc-channel-p target))))
f8db61b2
EZ
1624 (rcirc-record-activity (current-buffer)
1625 (when (not (rcirc-channel-p rcirc-target))
1626 'nick)))
2c8abe90 1627
8f10c937
DD
1628 (when (and rcirc-log-flag
1629 (or target
1630 rcirc-log-process-buffers))
195eca78
SM
1631 (rcirc-log process sender response target text))
1632
2c8abe90 1633 (sit-for 0) ; displayed text before hook
d1069532 1634 (run-hook-with-args 'rcirc-print-functions
2c8abe90 1635 process sender response target text)))))
bd43c990 1636
8f10c937
DD
1637(defun rcirc-generate-log-filename (process target)
1638 (if target
1639 (rcirc-generate-new-buffer-name process target)
1640 (process-name process)))
1641
1642(defcustom rcirc-log-filename-function 'rcirc-generate-log-filename
aacde24f
MB
1643 "A function to generate the filename used by rcirc's logging facility.
1644
1645It is called with two arguments, PROCESS and TARGET (see
1646`rcirc-generate-new-buffer-name' for their meaning), and should
1647return the filename, or nil if no logging is desired for this
1648session.
1649
1650If the returned filename is absolute (`file-name-absolute-p'
186ecaf1
DD
1651returns t), then it is used as-is, otherwise the resulting file
1652is put into `rcirc-log-directory'.
1653
1654The filename is then cleaned using `convert-standard-filename' to
1655guarantee valid filenames for the current OS."
aacde24f
MB
1656 :group 'rcirc
1657 :type 'function)
1658
195eca78
SM
1659(defun rcirc-log (process sender response target text)
1660 "Record line in `rcirc-log', to be later written to disk."
aacde24f
MB
1661 (let ((filename (funcall rcirc-log-filename-function process target)))
1662 (unless (null filename)
1663 (let ((cell (assoc-string filename rcirc-log-alist))
1664 (line (concat (format-time-string rcirc-time-format)
1665 (substring-no-properties
1666 (rcirc-format-response-string process sender
1667 response target text))
1668 "\n")))
1669 (if cell
1670 (setcdr cell (concat (cdr cell) line))
1671 (setq rcirc-log-alist
1672 (cons (cons filename line) rcirc-log-alist)))))))
195eca78
SM
1673
1674(defun rcirc-log-write ()
1675 "Flush `rcirc-log-alist' data to disk.
1676
aacde24f
MB
1677Log data is written to `rcirc-log-directory', except for
1678log-files with absolute names (see `rcirc-log-filename-function')."
195eca78 1679 (dolist (cell rcirc-log-alist)
186ecaf1
DD
1680 (let ((filename (convert-standard-filename
1681 (expand-file-name (car cell)
1682 rcirc-log-directory)))
aacde24f
MB
1683 (coding-system-for-write 'utf-8))
1684 (make-directory (file-name-directory filename) t)
1685 (with-temp-buffer
1686 (insert (cdr cell))
1687 (write-region (point-min) (point-max) filename t 'quiet))))
195eca78 1688 (setq rcirc-log-alist nil))
bd43c990 1689
d7a0fd6f
GM
1690(defun rcirc-view-log-file ()
1691 "View logfile corresponding to the current buffer."
1692 (interactive)
aebf69c8
DD
1693 (find-file-other-window
1694 (expand-file-name (funcall rcirc-log-filename-function
d7a0fd6f
GM
1695 (rcirc-buffer-process) rcirc-target)
1696 rcirc-log-directory)))
1697
bd43c990
RS
1698(defun rcirc-join-channels (process channels)
1699 "Join CHANNELS."
1700 (save-window-excursion
db58efbf
EZ
1701 (dolist (channel channels)
1702 (with-rcirc-process-buffer process
1703 (rcirc-cmd-join channel process)))))
bd43c990
RS
1704\f
1705;;; nick management
8216fbaf 1706(defvar rcirc-nick-prefix-chars "~&@%+")
bd43c990
RS
1707(defun rcirc-user-nick (user)
1708 "Return the nick from USER. Remove any non-nick junk."
db58efbf 1709 (save-match-data
8216fbaf
EZ
1710 (if (string-match (concat "^[" rcirc-nick-prefix-chars
1711 "]?\\([^! ]+\\)!?") (or user ""))
db58efbf
EZ
1712 (match-string 1 user)
1713 user)))
bd43c990 1714
bd43c990
RS
1715(defun rcirc-nick-channels (process nick)
1716 "Return list of channels for NICK."
db58efbf
EZ
1717 (with-rcirc-process-buffer process
1718 (mapcar (lambda (x) (car x))
1719 (gethash nick rcirc-nick-table))))
bd43c990 1720
a0a5c583
GM
1721(defun rcirc-put-nick-channel (process nick channel &optional line)
1722 "Add CHANNEL to list associated with NICK.
1723Update the associated linestamp if LINE is non-nil.
1724
1725If the record doesn't exist, and LINE is nil, set the linestamp
1726to zero."
2fbed782
EZ
1727 (let ((nick (rcirc-user-nick nick)))
1728 (with-rcirc-process-buffer process
1729 (let* ((chans (gethash nick rcirc-nick-table))
1730 (record (assoc-string channel chans t)))
1731 (if record
a0a5c583
GM
1732 (when line (setcdr record line))
1733 (puthash nick (cons (cons channel (or line 0))
2fbed782
EZ
1734 chans)
1735 rcirc-nick-table))))))
bd43c990
RS
1736
1737(defun rcirc-nick-remove (process nick)
1738 "Remove NICK from table."
adf794e4 1739 (with-rcirc-process-buffer process
bd43c990
RS
1740 (remhash nick rcirc-nick-table)))
1741
1742(defun rcirc-remove-nick-channel (process nick channel)
1743 "Remove the CHANNEL from list associated with NICK."
adf794e4 1744 (with-rcirc-process-buffer process
db58efbf 1745 (let* ((chans (gethash nick rcirc-nick-table))
adf794e4
EZ
1746 (newchans
1747 ;; instead of assoc-string-delete-all:
1748 (let ((record (assoc-string channel chans t)))
1749 (when record
1750 (setcar record 'delete)
1751 (assq-delete-all 'delete chans)))))
bd43c990
RS
1752 (if newchans
1753 (puthash nick newchans rcirc-nick-table)
1754 (remhash nick rcirc-nick-table)))))
1755
a2524d26
EZ
1756(defun rcirc-channel-nicks (process target)
1757 "Return the list of nicks associated with TARGET sorted by last activity."
1758 (when target
1759 (if (rcirc-channel-p target)
1760 (with-rcirc-process-buffer process
1761 (let (nicks)
1762 (maphash
1763 (lambda (k v)
1764 (let ((record (assoc-string target v t)))
1765 (if record
1766 (setq nicks (cons (cons k (cdr record)) nicks)))))
1767 rcirc-nick-table)
1768 (mapcar (lambda (x) (car x))
a0a5c583
GM
1769 (sort nicks (lambda (x y)
1770 (let ((lx (or (cdr x) 0))
1771 (ly (or (cdr y) 0)))
1772 (< ly lx)))))))
a2524d26 1773 (list target))))
2c8abe90
AS
1774
1775(defun rcirc-ignore-update-automatic (nick)
2e398771
JB
1776 "Remove NICK from `rcirc-ignore-list'
1777if NICK is also on `rcirc-ignore-list-automatic'."
2c8abe90
AS
1778 (when (member nick rcirc-ignore-list-automatic)
1779 (setq rcirc-ignore-list-automatic
1780 (delete nick rcirc-ignore-list-automatic)
1781 rcirc-ignore-list
1782 (delete nick rcirc-ignore-list))))
bd43c990 1783\f
c62bf05a 1784(defun rcirc-nickname< (s1 s2)
44ea155d
CY
1785 "Return t if IRC nickname S1 is less than S2, and nil otherwise.
1786Operator nicknames (@) are considered less than voiced
1787nicknames (+). Any other nicknames are greater than voiced
1788nicknames. The comparison is case-insensitive."
c62bf05a
DD
1789 (setq s1 (downcase s1)
1790 s2 (downcase s2))
1791 (let* ((s1-op (eq ?@ (string-to-char s1)))
1792 (s2-op (eq ?@ (string-to-char s2))))
1793 (if s1-op
1794 (if s2-op
1795 (string< (substring s1 1) (substring s2 1))
1796 t)
1797 (if s2-op
1798 nil
1799 (string< s1 s2)))))
1800
1801(defun rcirc-sort-nicknames-join (input sep)
44ea155d 1802 "Return a string of sorted nicknames.
c62bf05a 1803INPUT is a string containing nicknames separated by SEP.
44ea155d 1804This function does not alter the INPUT string."
a91dedc4
SM
1805 (let* ((parts (split-string input sep t))
1806 (sorted (sort parts 'rcirc-nickname<)))
1807 (mapconcat 'identity sorted sep)))
c62bf05a 1808\f
bd43c990 1809;;; activity tracking
b016851c
SM
1810(defvar rcirc-track-minor-mode-map
1811 (let ((map (make-sparse-keymap)))
1812 (define-key map (kbd "C-c C-@") 'rcirc-next-active-buffer)
1813 (define-key map (kbd "C-c C-SPC") 'rcirc-next-active-buffer)
1814 map)
db58efbf
EZ
1815 "Keymap for rcirc track minor mode.")
1816
e8f10ddb 1817;;;###autoload
db58efbf 1818(define-minor-mode rcirc-track-minor-mode
e1ac4066
GM
1819 "Global minor mode for tracking activity in rcirc buffers.
1820With a prefix argument ARG, enable the mode if ARG is positive,
1821and disable it otherwise. If called from Lisp, enable the mode
1822if ARG is omitted or nil."
db58efbf
EZ
1823 :init-value nil
1824 :lighter ""
1825 :keymap rcirc-track-minor-mode-map
1826 :global t
1827 :group 'rcirc
1828 (or global-mode-string (setq global-mode-string '("")))
1829 ;; toggle the mode-line channel indicator
1830 (if rcirc-track-minor-mode
a2524d26
EZ
1831 (progn
1832 (and (not (memq 'rcirc-activity-string global-mode-string))
1833 (setq global-mode-string
1834 (append global-mode-string '(rcirc-activity-string))))
1835 (add-hook 'window-configuration-change-hook
1836 'rcirc-window-configuration-change))
92c4adc1 1837 (setq global-mode-string
a2524d26
EZ
1838 (delete 'rcirc-activity-string global-mode-string))
1839 (remove-hook 'window-configuration-change-hook
1840 'rcirc-window-configuration-change)))
db58efbf 1841
adf794e4 1842(or (assq 'rcirc-ignore-buffer-activity-flag minor-mode-alist)
bd43c990 1843 (setq minor-mode-alist
adf794e4 1844 (cons '(rcirc-ignore-buffer-activity-flag " Ignore") minor-mode-alist)))
a2524d26
EZ
1845(or (assq 'rcirc-low-priority-flag minor-mode-alist)
1846 (setq minor-mode-alist
1847 (cons '(rcirc-low-priority-flag " LowPri") minor-mode-alist)))
195eca78
SM
1848(or (assq 'rcirc-omit-mode minor-mode-alist)
1849 (setq minor-mode-alist
1850 (cons '(rcirc-omit-mode " Omit") minor-mode-alist)))
bd43c990 1851
db58efbf
EZ
1852(defun rcirc-toggle-ignore-buffer-activity ()
1853 "Toggle the value of `rcirc-ignore-buffer-activity-flag'."
1854 (interactive)
1855 (setq rcirc-ignore-buffer-activity-flag
1856 (not rcirc-ignore-buffer-activity-flag))
1857 (message (if rcirc-ignore-buffer-activity-flag
1858 "Ignore activity in this buffer"
1859 "Notice activity in this buffer"))
bd43c990
RS
1860 (force-mode-line-update))
1861
a2524d26 1862(defun rcirc-toggle-low-priority ()
02f47e86 1863 "Toggle the value of `rcirc-low-priority-flag'."
a2524d26
EZ
1864 (interactive)
1865 (setq rcirc-low-priority-flag
1866 (not rcirc-low-priority-flag))
1867 (message (if rcirc-low-priority-flag
1868 "Activity in this buffer is low priority"
1869 "Activity in this buffer is normal priority"))
1870 (force-mode-line-update))
1871
195eca78
SM
1872(defun rcirc-omit-mode ()
1873 "Toggle the Rcirc-Omit mode.
0ffab1eb 1874If enabled, \"uninteresting\" lines are not shown.
195eca78
SM
1875Uninteresting lines are those whose responses are listed in
1876`rcirc-omit-responses'."
1877 (interactive)
1878 (setq rcirc-omit-mode (not rcirc-omit-mode))
a0a5c583
GM
1879 (if rcirc-omit-mode
1880 (progn
1230c3cb 1881 (add-to-invisibility-spec '(rcirc-omit . nil))
a0a5c583 1882 (message "Rcirc-Omit mode enabled"))
1230c3cb 1883 (remove-from-invisibility-spec '(rcirc-omit . nil))
a0a5c583
GM
1884 (message "Rcirc-Omit mode disabled"))
1885 (recenter (when (> (point) rcirc-prompt-start-marker) -1)))
bd43c990
RS
1886
1887(defun rcirc-switch-to-server-buffer ()
1888 "Switch to the server buffer associated with current channel buffer."
1889 (interactive)
488086f4
SM
1890 (unless (buffer-live-p rcirc-server-buffer)
1891 (error "No such buffer"))
195eca78 1892 (switch-to-buffer rcirc-server-buffer))
bd43c990
RS
1893
1894(defun rcirc-jump-to-first-unread-line ()
1895 "Move the point to the first unread line in this buffer."
1896 (interactive)
195eca78
SM
1897 (if (marker-position overlay-arrow-position)
1898 (goto-char overlay-arrow-position)
1899 (message "No unread messages")))
1900
1901(defun rcirc-non-irc-buffer ()
1902 (let ((buflist (buffer-list))
1903 buffer)
1904 (while (and buflist (not buffer))
1905 (with-current-buffer (car buflist)
1906 (unless (or (eq major-mode 'rcirc-mode)
1907 (= ?\s (aref (buffer-name) 0)) ; internal buffers
1908 (get-buffer-window (current-buffer)))
1909 (setq buffer (current-buffer))))
1910 (setq buflist (cdr buflist)))
1911 buffer))
bd43c990
RS
1912
1913(defun rcirc-next-active-buffer (arg)
195eca78
SM
1914 "Switch to the next rcirc buffer with activity.
1915With prefix ARG, go to the next low priority buffer with activity."
a2524d26
EZ
1916 (interactive "P")
1917 (let* ((pair (rcirc-split-activity rcirc-activity))
1918 (lopri (car pair))
92c4adc1 1919 (hipri (cdr pair)))
a2524d26
EZ
1920 (if (or (and (not arg) hipri)
1921 (and arg lopri))
a0a5c583
GM
1922 (progn
1923 (switch-to-buffer (car (if arg lopri hipri)))
1924 (when (> (point) rcirc-prompt-start-marker)
1925 (recenter -1)))
a2524d26 1926 (if (eq major-mode 'rcirc-mode)
195eca78 1927 (switch-to-buffer (rcirc-non-irc-buffer))
274f1353
DK
1928 (message "%s" (concat
1929 "No IRC activity."
1930 (when lopri
1931 (concat
1932 " Type C-u "
1933 (key-description (this-command-keys))
1934 " for low priority activity."))))))))
bd43c990 1935
d1069532
SM
1936(define-obsolete-variable-alias 'rcirc-activity-hooks
1937 'rcirc-activity-functions "24.3")
1938(defvar rcirc-activity-functions nil
bd43c990
RS
1939 "Hook to be run when there is channel activity.
1940
1941Functions are called with a single argument, the buffer with the
1942activity. Only run if the buffer is not visible and
adf794e4 1943`rcirc-ignore-buffer-activity-flag' is non-nil.")
bd43c990 1944
a2524d26 1945(defun rcirc-record-activity (buffer &optional type)
bd43c990
RS
1946 "Record BUFFER activity with TYPE."
1947 (with-current-buffer buffer
195eca78
SM
1948 (let ((old-activity rcirc-activity)
1949 (old-types rcirc-activity-types))
1950 (when (not (get-buffer-window (current-buffer) t))
1951 (setq rcirc-activity
1952 (sort (add-to-list 'rcirc-activity (current-buffer))
1953 (lambda (b1 b2)
1954 (let ((t1 (with-current-buffer b1 rcirc-last-post-time))
1955 (t2 (with-current-buffer b2 rcirc-last-post-time)))
1956 (time-less-p t2 t1)))))
1957 (pushnew type rcirc-activity-types)
1958 (unless (and (equal rcirc-activity old-activity)
1959 (member type old-types))
1960 (rcirc-update-activity-string)))))
d1069532 1961 (run-hook-with-args 'rcirc-activity-functions buffer))
bd43c990
RS
1962
1963(defun rcirc-clear-activity (buffer)
1964 "Clear the BUFFER activity."
0ffab1eb 1965 (setq rcirc-activity (remove buffer rcirc-activity))
bd43c990 1966 (with-current-buffer buffer
f8db61b2 1967 (setq rcirc-activity-types nil)))
bd43c990 1968
195eca78
SM
1969(defun rcirc-clear-unread (buffer)
1970 "Erase the last read message arrow from BUFFER."
1971 (when (buffer-live-p buffer)
1972 (with-current-buffer buffer
1973 (set-marker overlay-arrow-position nil))))
1974
a2524d26
EZ
1975(defun rcirc-split-activity (activity)
1976 "Return a cons cell with ACTIVITY split into (lopri . hipri)."
1977 (let (lopri hipri)
1978 (dolist (buf rcirc-activity)
1979 (with-current-buffer buf
1980 (if (and rcirc-low-priority-flag
f8db61b2 1981 (not (member 'nick rcirc-activity-types)))
a2524d26
EZ
1982 (add-to-list 'lopri buf t)
1983 (add-to-list 'hipri buf t))))
1984 (cons lopri hipri)))
1985
195eca78
SM
1986(defvar rcirc-update-activity-string-hook nil
1987 "Hook run whenever the activity string is updated.")
1988
adf794e4 1989;; TODO: add mouse properties
bd43c990
RS
1990(defun rcirc-update-activity-string ()
1991 "Update mode-line string."
a2524d26
EZ
1992 (let* ((pair (rcirc-split-activity rcirc-activity))
1993 (lopri (car pair))
1994 (hipri (cdr pair)))
1995 (setq rcirc-activity-string
7faa3f8c 1996 (cond ((or hipri lopri)
195eca78 1997 (concat (and hipri "[")
7faa3f8c
MB
1998 (rcirc-activity-string hipri)
1999 (and hipri lopri ",")
2000 (and lopri
2001 (concat "("
2002 (rcirc-activity-string lopri)
2003 ")"))
195eca78 2004 (and hipri "]")))
7faa3f8c 2005 ((not (null (rcirc-process-list)))
195eca78
SM
2006 "[]")
2007 (t "[]")))
2008 (run-hooks 'rcirc-update-activity-string-hook)))
a2524d26
EZ
2009
2010(defun rcirc-activity-string (buffers)
2011 (mapconcat (lambda (b)
f8db61b2 2012 (let ((s (substring-no-properties (rcirc-short-buffer-name b))))
a2524d26 2013 (with-current-buffer b
f8db61b2
EZ
2014 (dolist (type rcirc-activity-types)
2015 (rcirc-add-face 0 (length s)
2016 (case type
82741a5e
CY
2017 (nick 'rcirc-track-nick)
2018 (keyword 'rcirc-track-keyword))
f8db61b2
EZ
2019 s)))
2020 s))
a2524d26 2021 buffers ","))
bd43c990
RS
2022
2023(defun rcirc-short-buffer-name (buffer)
37269466 2024 "Return a short name for BUFFER to use in the mode line indicator."
bd43c990 2025 (with-current-buffer buffer
adf794e4
EZ
2026 (or rcirc-short-buffer-name (buffer-name))))
2027
195eca78
SM
2028(defun rcirc-visible-buffers ()
2029 "Return a list of the visible buffers that are in rcirc-mode."
2030 (let (acc)
adf794e4 2031 (walk-windows (lambda (w)
195eca78
SM
2032 (with-current-buffer (window-buffer w)
2033 (when (eq major-mode 'rcirc-mode)
2034 (push (current-buffer) acc)))))
2035 acc))
2036
2037(defvar rcirc-visible-buffers nil)
2038(defun rcirc-window-configuration-change ()
2039 (unless (minibuffer-window-active-p (minibuffer-window))
2040 ;; delay this until command has finished to make sure window is
2041 ;; actually visible before clearing activity
2042 (add-hook 'post-command-hook 'rcirc-window-configuration-change-1)))
2043
2044(defun rcirc-window-configuration-change-1 ()
2045 ;; clear activity and overlay arrows
2046 (let* ((old-activity rcirc-activity)
2047 (hidden-buffers rcirc-visible-buffers))
2048
2049 (setq rcirc-visible-buffers (rcirc-visible-buffers))
2050
2051 (dolist (vbuf rcirc-visible-buffers)
2052 (setq hidden-buffers (delq vbuf hidden-buffers))
2053 ;; clear activity for all visible buffers
2054 (rcirc-clear-activity vbuf))
2055
2056 ;; clear unread arrow from recently hidden buffers
2057 (dolist (hbuf hidden-buffers)
2058 (rcirc-clear-unread hbuf))
2059
2060 ;; remove any killed buffers from list
2061 (setq rcirc-activity
2062 (delq nil (mapcar (lambda (buf) (when (buffer-live-p buf) buf))
2063 rcirc-activity)))
2064 ;; update the mode-line string
2065 (unless (equal old-activity rcirc-activity)
2066 (rcirc-update-activity-string)))
2067
2068 (remove-hook 'post-command-hook 'rcirc-window-configuration-change-1))
bd43c990
RS
2069
2070\f
adf794e4
EZ
2071;;; buffer name abbreviation
2072(defun rcirc-update-short-buffer-names ()
2073 (let ((bufalist
2074 (apply 'append (mapcar (lambda (process)
2075 (with-rcirc-process-buffer process
2076 rcirc-buffer-alist))
2077 (rcirc-process-list)))))
2078 (dolist (i (rcirc-abbreviate bufalist))
a2524d26
EZ
2079 (when (buffer-live-p (cdr i))
2080 (with-current-buffer (cdr i)
2081 (setq rcirc-short-buffer-name (car i)))))))
adf794e4
EZ
2082
2083(defun rcirc-abbreviate (pairs)
2084 (apply 'append (mapcar 'rcirc-rebuild-tree (rcirc-make-trees pairs))))
2085
2086(defun rcirc-rebuild-tree (tree &optional acc)
2087 (let ((ch (char-to-string (car tree))))
2088 (dolist (x (cdr tree))
2089 (if (listp x)
2090 (setq acc (append acc
2091 (mapcar (lambda (y)
2092 (cons (concat ch (car y))
2093 (cdr y)))
2094 (rcirc-rebuild-tree x))))
2095 (setq acc (cons (cons ch x) acc))))
2096 acc))
2097
2098(defun rcirc-make-trees (pairs)
2099 (let (alist)
2100 (mapc (lambda (pair)
2101 (if (consp pair)
2102 (let* ((str (car pair))
2103 (data (cdr pair))
2104 (char (unless (zerop (length str))
2105 (aref str 0)))
2106 (rest (unless (zerop (length str))
2107 (substring str 1)))
2108 (part (if char (assq char alist))))
2109 (if part
2110 ;; existing partition
2111 (setcdr part (cons (cons rest data) (cdr part)))
2112 ;; new partition
2113 (setq alist (cons (if char
2114 (list char (cons rest data))
2115 data)
2116 alist))))
2117 (setq alist (cons pair alist))))
2118 pairs)
2119 ;; recurse into cdrs of alist
2120 (mapc (lambda (x)
2121 (when (and (listp x) (listp (cadr x)))
2122 (setcdr x (if (> (length (cdr x)) 1)
2123 (rcirc-make-trees (cdr x))
2124 (setcdr x (list (cdadr x)))))))
2125 alist)))
2126\f
bd43c990
RS
2127;;; /commands these are called with 3 args: PROCESS, TARGET, which is
2128;; the current buffer/channel/user, and ARGS, which is a string
2129;; containing the text following the /cmd.
2130
adf794e4 2131(defmacro defun-rcirc-command (command argument docstring interactive-form
94c7243b 2132 &rest body)
bd43c990 2133 "Define a command."
94c7243b
LL
2134 `(progn
2135 (add-to-list 'rcirc-client-commands ,(concat "/" (symbol-name command)))
2136 (defun ,(intern (concat "rcirc-cmd-" (symbol-name command)))
2137 (,@argument &optional process target)
2138 ,(concat docstring "\n\nNote: If PROCESS or TARGET are nil, the values given"
2139 "\nby `rcirc-buffer-process' and `rcirc-target' will be used.")
2140 ,interactive-form
2141 (let ((process (or process (rcirc-buffer-process)))
2142 (target (or target rcirc-target)))
2143 ,@body))))
bd43c990
RS
2144
2145(defun-rcirc-command msg (message)
2146 "Send private MESSAGE to TARGET."
2147 (interactive "i")
2148 (if (null message)
2149 (progn
2150 (setq target (completing-read "Message nick: "
92c4adc1 2151 (with-rcirc-server-buffer
a2524d26 2152 rcirc-nick-table)))
bd43c990
RS
2153 (when (> (length target) 0)
2154 (setq message (read-string (format "Message %s: " target)))
2155 (when (> (length message) 0)
2156 (rcirc-send-message process target message))))
2157 (if (not (string-match "\\([^ ]+\\) \\(.+\\)" message))
2158 (message "Not enough args, or something.")
2159 (setq target (match-string 1 message)
2160 message (match-string 2 message))
2161 (rcirc-send-message process target message))))
2162
2163(defun-rcirc-command query (nick)
2164 "Open a private chat buffer to NICK."
2165 (interactive (list (completing-read "Query nick: "
a2524d26 2166 (with-rcirc-server-buffer rcirc-nick-table))))
adf794e4
EZ
2167 (let ((existing-buffer (rcirc-get-buffer process nick)))
2168 (switch-to-buffer (or existing-buffer
2169 (rcirc-get-buffer-create process nick)))
2170 (when (not existing-buffer)
bd43c990
RS
2171 (rcirc-cmd-whois nick))))
2172
e0e36cac
DD
2173(defun-rcirc-command join (channels)
2174 "Join CHANNELS.
2175CHANNELS is a comma- or space-separated string of channel names."
2176 (interactive "sJoin channels: ")
2177 (let* ((split-channels (split-string channels "[ ,]" t))
2178 (buffers (mapcar (lambda (ch)
2179 (rcirc-get-buffer-create process ch))
fcd8ed1d
DD
2180 split-channels))
2181 (channels (mapconcat 'identity split-channels ",")))
e0e36cac 2182 (rcirc-send-string process (concat "JOIN " channels))
bd43c990 2183 (when (not (eq (selected-window) (minibuffer-window)))
e0e36cac
DD
2184 (dolist (b buffers) ;; order the new channel buffers in the buffer list
2185 (switch-to-buffer b)))))
bd43c990 2186
567457e3
LL
2187(defun-rcirc-command invite (nick-channel)
2188 "Invite NICK to CHANNEL."
2189 (interactive (list
2190 (concat
2191 (completing-read "Invite nick: "
2192 (with-rcirc-server-buffer rcirc-nick-table))
2193 " "
2194 (read-string "Channel: "))))
2195 (rcirc-send-string process (concat "INVITE " nick-channel)))
2196
195eca78 2197;; TODO: /part #channel reason, or consider removing #channel altogether
bd43c990
RS
2198(defun-rcirc-command part (channel)
2199 "Part CHANNEL."
2200 (interactive "sPart channel: ")
2201 (let ((channel (if (> (length channel) 0) channel target)))
adf794e4 2202 (rcirc-send-string process (concat "PART " channel " :" rcirc-id-string))))
bd43c990 2203
5c14e333
CY
2204(defun-rcirc-command quit (reason)
2205 "Send a quit message to server with REASON."
2206 (interactive "sQuit reason: ")
2207 (rcirc-send-string process (concat "QUIT :"
2208 (if (not (zerop (length reason)))
2209 reason
2210 rcirc-id-string))))
bd43c990
RS
2211
2212(defun-rcirc-command nick (nick)
2213 "Change nick to NICK."
2214 (interactive "i")
2215 (when (null nick)
2216 (setq nick (read-string "New nick: " (rcirc-nick process))))
2217 (rcirc-send-string process (concat "NICK " nick)))
2218
2219(defun-rcirc-command names (channel)
2220 "Display list of names in CHANNEL or in current channel if CHANNEL is nil.
2221If called interactively, prompt for a channel when prefix arg is supplied."
2222 (interactive "P")
32226619 2223 (if (called-interactively-p 'interactive)
bd43c990
RS
2224 (if channel
2225 (setq channel (read-string "List names in channel: " target))))
2226 (let ((channel (if (> (length channel) 0)
2227 channel
2228 target)))
2229 (rcirc-send-string process (concat "NAMES " channel))))
2230
2231(defun-rcirc-command topic (topic)
2232 "List TOPIC for the TARGET channel.
2233With a prefix arg, prompt for new topic."
2234 (interactive "P")
32226619 2235 (if (and (called-interactively-p 'interactive) topic)
bd43c990
RS
2236 (setq topic (read-string "New Topic: " rcirc-topic)))
2237 (rcirc-send-string process (concat "TOPIC " target
2238 (when (> (length topic) 0)
2239 (concat " :" topic)))))
2240
2241(defun-rcirc-command whois (nick)
2242 "Request information from server about NICK."
2243 (interactive (list
2244 (completing-read "Whois: "
a2524d26 2245 (with-rcirc-server-buffer rcirc-nick-table))))
bd43c990
RS
2246 (rcirc-send-string process (concat "WHOIS " nick)))
2247
2248(defun-rcirc-command mode (args)
2249 "Set mode with ARGS."
2250 (interactive (list (concat (read-string "Mode nick or channel: ")
2251 " " (read-string "Mode: "))))
2252 (rcirc-send-string process (concat "MODE " args)))
2253
2254(defun-rcirc-command list (channels)
2255 "Request information on CHANNELS from server."
2256 (interactive "sList Channels: ")
2257 (rcirc-send-string process (concat "LIST " channels)))
2258
2259(defun-rcirc-command oper (args)
2260 "Send operator command to server."
2261 (interactive "sOper args: ")
2262 (rcirc-send-string process (concat "OPER " args)))
2263
2264(defun-rcirc-command quote (message)
2265 "Send MESSAGE literally to server."
2266 (interactive "sServer message: ")
2267 (rcirc-send-string process message))
2268
2269(defun-rcirc-command kick (arg)
2270 "Kick NICK from current channel."
2271 (interactive (list
2272 (concat (completing-read "Kick nick: "
92c4adc1 2273 (rcirc-channel-nicks
a2524d26
EZ
2274 (rcirc-buffer-process)
2275 rcirc-target))
bd43c990
RS
2276 (read-from-minibuffer "Kick reason: "))))
2277 (let* ((arglist (split-string arg))
adf794e4 2278 (argstring (concat (car arglist) " :"
bd43c990
RS
2279 (mapconcat 'identity (cdr arglist) " "))))
2280 (rcirc-send-string process (concat "KICK " target " " argstring))))
2281
2282(defun rcirc-cmd-ctcp (args &optional process target)
2283 (if (string-match "^\\([^ ]+\\)\\s-+\\(.+\\)$" args)
ee6a57ab
DD
2284 (let* ((target (match-string 1 args))
2285 (request (upcase (match-string 2 args)))
2286 (function (intern-soft (concat "rcirc-ctcp-sender-" request))))
2287 (if (fboundp function) ;; use special function if available
2288 (funcall function process target request)
1be1d1e9 2289 (rcirc-send-ctcp process target request)))
adf794e4 2290 (rcirc-print process (rcirc-nick process) "ERROR" nil
bd43c990
RS
2291 "usage: /ctcp NICK REQUEST")))
2292
ee6a57ab
DD
2293(defun rcirc-ctcp-sender-PING (process target request)
2294 "Send a CTCP PING message to TARGET."
73057ba9 2295 (let ((timestamp (format "%.0f" (rcirc-float-time))))
1be1d1e9 2296 (rcirc-send-ctcp process target "PING" timestamp)))
ee6a57ab 2297
bd43c990 2298(defun rcirc-cmd-me (args &optional process target)
1be1d1e9 2299 (rcirc-send-ctcp process target "ACTION" args))
2c8abe90 2300
c00725d7
LL
2301(defun rcirc-add-or-remove (set &rest elements)
2302 (dolist (elt elements)
2303 (if (and elt (not (string= "" elt)))
2304 (setq set (if (member-ignore-case elt set)
2305 (delete elt set)
2306 (cons elt set)))))
2307 set)
f8db61b2 2308
2c8abe90
AS
2309(defun-rcirc-command ignore (nick)
2310 "Manage the ignore list.
2311Ignore NICK, unignore NICK if already ignored, or list ignored
2312nicks when no NICK is given. When listing ignored nicks, the
2e398771 2313ones added to the list automatically are marked with an asterisk."
2c8abe90 2314 (interactive "sToggle ignoring of nick: ")
c00725d7
LL
2315 (setq rcirc-ignore-list
2316 (apply #'rcirc-add-or-remove rcirc-ignore-list
2317 (split-string nick nil t)))
92c4adc1 2318 (rcirc-print process nil "IGNORE" target
db58efbf
EZ
2319 (mapconcat
2320 (lambda (nick)
2321 (concat nick
2322 (if (member nick rcirc-ignore-list-automatic)
2323 "*" "")))
2324 rcirc-ignore-list " ")))
2c8abe90 2325
f8db61b2
EZ
2326(defun-rcirc-command bright (nick)
2327 "Manage the bright nick list."
2328 (interactive "sToggle emphasis of nick: ")
c00725d7
LL
2329 (setq rcirc-bright-nicks
2330 (apply #'rcirc-add-or-remove rcirc-bright-nicks
2331 (split-string nick nil t)))
92c4adc1 2332 (rcirc-print process nil "BRIGHT" target
f8db61b2
EZ
2333 (mapconcat 'identity rcirc-bright-nicks " ")))
2334
2335(defun-rcirc-command dim (nick)
2336 "Manage the dim nick list."
2337 (interactive "sToggle deemphasis of nick: ")
c00725d7
LL
2338 (setq rcirc-dim-nicks
2339 (apply #'rcirc-add-or-remove rcirc-dim-nicks
2340 (split-string nick nil t)))
92c4adc1 2341 (rcirc-print process nil "DIM" target
f8db61b2
EZ
2342 (mapconcat 'identity rcirc-dim-nicks " ")))
2343
2344(defun-rcirc-command keyword (keyword)
2345 "Manage the keyword list.
2346Mark KEYWORD, unmark KEYWORD if already marked, or list marked
2347keywords when no KEYWORD is given."
2348 (interactive "sToggle highlighting of keyword: ")
c00725d7
LL
2349 (setq rcirc-keywords
2350 (apply #'rcirc-add-or-remove rcirc-keywords
2351 (split-string keyword nil t)))
92c4adc1 2352 (rcirc-print process nil "KEYWORD" target
f8db61b2
EZ
2353 (mapconcat 'identity rcirc-keywords " ")))
2354
bd43c990 2355\f
f8db61b2
EZ
2356(defun rcirc-add-face (start end name &optional object)
2357 "Add face NAME to the face text property of the text from START to END."
2358 (when name
2359 (let ((pos start)
2360 next prop)
2361 (while (< pos end)
2362 (setq prop (get-text-property pos 'face object)
2363 next (next-single-property-change pos 'face object end))
2364 (unless (member name (get-text-property pos 'face object))
2365 (add-text-properties pos next (list 'face (cons name prop)) object))
2366 (setq pos next)))))
adf794e4 2367
bd43c990
RS
2368(defun rcirc-facify (string face)
2369 "Return a copy of STRING with FACE property added."
f8db61b2
EZ
2370 (let ((string (or string "")))
2371 (rcirc-add-face 0 (length string) face string)
2372 string))
bd43c990 2373
bd43c990 2374(defvar rcirc-url-regexp
73dd622f
RS
2375 (concat
2376 "\\b\\(\\(www\\.\\|\\(s?https?\\|ftp\\|file\\|gopher\\|"
2377 "nntp\\|news\\|telnet\\|wais\\|mailto\\|info\\):\\)"
2378 "\\(//[-a-z0-9_.]+:[0-9]*\\)?"
2379 (if (string-match "[[:digit:]]" "1") ;; Support POSIX?
2380 (let ((chars "-a-z0-9_=#$@~%&*+\\/[:word:]")
2381 (punct "!?:;.,"))
2382 (concat
2383 "\\(?:"
2384 ;; Match paired parentheses, e.g. in Wikipedia URLs:
2385 "[" chars punct "]+" "(" "[" chars punct "]+" "[" chars "]*)" "[" chars "]"
2386 "\\|"
2387 "[" chars punct "]+" "[" chars "]"
2388 "\\)"))
2389 (concat ;; XEmacs 21.4 doesn't support POSIX.
2390 "\\([-a-z0-9_=!?#$@~%&*+\\/:;.,]\\|\\w\\)+"
2391 "\\([-a-z0-9_=#$@~%&*+\\/]\\|\\w\\)"))
2392 "\\)")
2e398771 2393 "Regexp matching URLs. Set to nil to disable URL features in rcirc.")
bd43c990
RS
2394
2395(defun rcirc-browse-url (&optional arg)
2e398771 2396 "Prompt for URL to browse based on URLs in buffer."
02f47e86 2397 (interactive "P")
bd43c990
RS
2398 (let ((completions (mapcar (lambda (x) (cons x nil)) rcirc-urls))
2399 (initial-input (car rcirc-urls))
2400 (history (cdr rcirc-urls)))
2401 (browse-url (completing-read "rcirc browse-url: "
2402 completions nil nil initial-input 'history)
2403 arg)))
f8db61b2 2404\f
195eca78
SM
2405(defun rcirc-markup-timestamp (sender response)
2406 (goto-char (point-min))
0ffab1eb 2407 (insert (rcirc-facify (format-time-string rcirc-time-format)
195eca78 2408 'rcirc-timestamp)))
f8db61b2 2409
195eca78 2410(defun rcirc-markup-attributes (sender response)
f8db61b2
EZ
2411 (while (re-search-forward "\\([\C-b\C-_\C-v]\\).*?\\(\\1\\|\C-o\\)" nil t)
2412 (rcirc-add-face (match-beginning 0) (match-end 0)
2413 (case (char-after (match-beginning 1))
2414 (?\C-b 'bold)
2415 (?\C-v 'italic)
2416 (?\C-_ 'underline)))
2417 ;; keep the ^O since it could terminate other attributes
2418 (when (not (eq ?\C-o (char-before (match-end 2))))
2419 (delete-region (match-beginning 2) (match-end 2)))
2420 (delete-region (match-beginning 1) (match-end 1))
08fc78fe 2421 (goto-char (match-beginning 1)))
f8db61b2 2422 ;; remove the ^O characters now
142b4d90 2423 (goto-char (point-min))
f8db61b2
EZ
2424 (while (re-search-forward "\C-o+" nil t)
2425 (delete-region (match-beginning 0) (match-end 0))))
2426
195eca78 2427(defun rcirc-markup-my-nick (sender response)
f8db61b2 2428 (with-syntax-table rcirc-nick-syntax-table
0ffab1eb
TTN
2429 (while (re-search-forward (concat "\\b"
2430 (regexp-quote (rcirc-nick
195eca78 2431 (rcirc-buffer-process)))
f8db61b2
EZ
2432 "\\b")
2433 nil t)
92c4adc1 2434 (rcirc-add-face (match-beginning 0) (match-end 0)
f8db61b2
EZ
2435 'rcirc-nick-in-message)
2436 (when (string= response "PRIVMSG")
0ffab1eb 2437 (rcirc-add-face (point-min) (point-max)
195eca78
SM
2438 'rcirc-nick-in-message-full-line)
2439 (rcirc-record-activity (current-buffer) 'nick)))))
f8db61b2 2440
195eca78 2441(defun rcirc-markup-urls (sender response)
9ff90d99
DD
2442 (while (and rcirc-url-regexp ;; nil means disable URL catching
2443 (re-search-forward rcirc-url-regexp nil t))
f8db61b2 2444 (let ((start (match-beginning 0))
1ddd96f5
LL
2445 (end (match-end 0))
2446 (url (match-string-no-properties 0)))
2447 (make-button start end
2448 'face 'rcirc-url
2449 'follow-link t
2450 'rcirc-url url
2451 'action (lambda (button)
2452 (browse-url (button-get button 'rcirc-url))))
f8db61b2 2453 ;; record the url
1ddd96f5 2454 (push url rcirc-urls))))
195eca78
SM
2455
2456(defun rcirc-markup-keywords (sender response)
2457 (when (and (string= response "PRIVMSG")
2458 (not (string= sender (rcirc-nick (rcirc-buffer-process)))))
2459 (let* ((target (or rcirc-target ""))
2460 (keywords (delq nil (mapcar (lambda (keyword)
2461 (when (not (string-match keyword
2462 target))
2463 keyword))
2464 rcirc-keywords))))
2465 (when keywords
2466 (while (re-search-forward (regexp-opt keywords 'words) nil t)
2467 (rcirc-add-face (match-beginning 0) (match-end 0) 'rcirc-keyword)
2468 (rcirc-record-activity (current-buffer) 'keyword))))))
2469
2470(defun rcirc-markup-bright-nicks (sender response)
f8db61b2
EZ
2471 (when (and rcirc-bright-nicks
2472 (string= response "NAMES"))
2473 (with-syntax-table rcirc-nick-syntax-table
2474 (while (re-search-forward (regexp-opt rcirc-bright-nicks 'words) nil t)
2475 (rcirc-add-face (match-beginning 0) (match-end 0)
2476 'rcirc-bright-nick)))))
195eca78
SM
2477
2478(defun rcirc-markup-fill (sender response)
2479 (when (not (string= response "372")) ; /motd
2480 (let ((fill-prefix
2481 (or rcirc-fill-prefix
2482 (make-string (- (point) (line-beginning-position)) ?\s)))
683b7dc6
GM
2483 (fill-column (- (cond ((eq rcirc-fill-column 'frame-width)
2484 (1- (frame-width)))
2485 (rcirc-fill-column
2486 rcirc-fill-column)
2487 (t fill-column))
2488 ;; make sure ... doesn't cause line wrapping
aebf69c8 2489 3)))
195eca78 2490 (fill-region (point) (point-max) nil t))))
bd43c990
RS
2491\f
2492;;; handlers
2493;; these are called with the server PROCESS, the SENDER, which is a
2494;; server or a user, depending on the command, the ARGS, which is a
2495;; list of strings, and the TEXT, which is the original server text,
2496;; verbatim
2497(defun rcirc-handler-001 (process sender args text)
2498 (rcirc-handler-generic process "001" sender args text)
adf794e4 2499 (with-rcirc-process-buffer process
8216fbaf
EZ
2500 (setq rcirc-connecting nil)
2501 (rcirc-reschedule-timeout process)
2502 (setq rcirc-server-name sender)
bd43c990
RS
2503 (setq rcirc-nick (car args))
2504 (rcirc-update-prompt)
72d2c2e3 2505 (if rcirc-auto-authenticate-flag
221ddf68
TH
2506 (if (and rcirc-authenticate-before-join
2507 ;; We have to ensure that there's an authentication
2508 ;; entry for that server. Else,
2509 ;; rcirc-authenticated-hook won't be triggered, and
2510 ;; autojoin won't happen at all.
2511 (let (auth-required)
2512 (dolist (s rcirc-authinfo auth-required)
2513 (when (string-match (car s) rcirc-server-name)
2514 (setq auth-required t)))))
72d2c2e3 2515 (progn
c47971d7 2516 (add-hook 'rcirc-authenticated-hook 'rcirc-join-channels-post-auth t t)
72d2c2e3
DD
2517 (rcirc-authenticate))
2518 (rcirc-authenticate)
2519 (rcirc-join-channels process rcirc-startup-channels))
2520 (rcirc-join-channels process rcirc-startup-channels))))
2521
2522(defun rcirc-join-channels-post-auth (process)
2523 "Join `rcirc-startup-channels' after authenticating."
2524 (with-rcirc-process-buffer process
adf794e4 2525 (rcirc-join-channels process rcirc-startup-channels)))
bd43c990
RS
2526
2527(defun rcirc-handler-PRIVMSG (process sender args text)
72d2c2e3 2528 (rcirc-check-auth-status process sender args text)
bd43c990
RS
2529 (let ((target (if (rcirc-channel-p (car args))
2530 (car args)
db58efbf 2531 sender))
bd43c990
RS
2532 (message (or (cadr args) "")))
2533 (if (string-match "^\C-a\\(.*\\)\C-a$" message)
2534 (rcirc-handler-CTCP process target sender (match-string 1 message))
2535 (rcirc-print process sender "PRIVMSG" target message t))
a0a5c583
GM
2536 ;; update nick linestamp
2537 (with-current-buffer (rcirc-get-buffer process target t)
2538 (rcirc-put-nick-channel process sender target rcirc-current-line))))
bd43c990
RS
2539
2540(defun rcirc-handler-NOTICE (process sender args text)
72d2c2e3 2541 (rcirc-check-auth-status process sender args text)
bd43c990
RS
2542 (let ((target (car args))
2543 (message (cadr args)))
adf794e4
EZ
2544 (if (string-match "^\C-a\\(.*\\)\C-a$" message)
2545 (rcirc-handler-CTCP-response process target sender
2546 (match-string 1 message))
2547 (rcirc-print process sender "NOTICE"
2548 (cond ((rcirc-channel-p target)
2549 target)
2550 ;;; -ChanServ- [#gnu] Welcome...
02f47e86 2551 ((string-match "\\[\\(#[^\] ]+\\)\\]" message)
adf794e4
EZ
2552 (match-string 1 message))
2553 (sender
a2524d26 2554 (if (string= sender (rcirc-server-name process))
db58efbf
EZ
2555 nil ; server notice
2556 sender)))
adf794e4 2557 message t))))
bd43c990 2558
72d2c2e3
DD
2559(defun rcirc-check-auth-status (process sender args text)
2560 "Check if the user just authenticated.
2561If authenticated, runs `rcirc-authenticated-hook' with PROCESS as
2562the only argument."
2563 (with-rcirc-process-buffer process
2564 (when (and (not rcirc-user-authenticated)
2565 rcirc-authenticate-before-join
2566 rcirc-auto-authenticate-flag)
2567 (let ((target (car args))
2568 (message (cadr args)))
2569 (when (or
2570 (and ;; nickserv
2571 (string= sender "NickServ")
2572 (string= target rcirc-nick)
2573 (member message
2574 (list
2575 (format "You are now identified for \C-b%s\C-b." rcirc-nick)
35b1c40c 2576 (format "You are successfully identified as \C-b%s\C-b." rcirc-nick)
72d2c2e3
DD
2577 "Password accepted - you are now recognized."
2578 )))
77f63d30
DD
2579 (and ;; quakenet
2580 (string= sender "Q")
2581 (string= target rcirc-nick)
c47971d7 2582 (string-match "\\`You are now logged in as .+\\.\\'" message)))
72d2c2e3
DD
2583 (setq rcirc-user-authenticated t)
2584 (run-hook-with-args 'rcirc-authenticated-hook process)
2585 (remove-hook 'rcirc-authenticated-hook 'rcirc-join-channels-post-auth t))))))
2586
bd43c990 2587(defun rcirc-handler-WALLOPS (process sender args text)
db58efbf 2588 (rcirc-print process sender "WALLOPS" sender (car args) t))
bd43c990
RS
2589
2590(defun rcirc-handler-JOIN (process sender args text)
db58efbf 2591 (let ((channel (car args)))
a0a5c583
GM
2592 (with-current-buffer (rcirc-get-buffer-create process channel)
2593 ;; when recently rejoining, restore the linestamp
2594 (rcirc-put-nick-channel process sender channel
2595 (let ((last-activity-lines
683b7dc6 2596 (rcirc-elapsed-lines process sender channel)))
a0a5c583
GM
2597 (when (and last-activity-lines
2598 (< last-activity-lines rcirc-omit-threshold))
827b77e9
DD
2599 (rcirc-last-line process sender channel))))
2600 ;; reset mode-line-process in case joining a channel with an
2601 ;; already open buffer (after getting kicked e.g.)
2602 (setq mode-line-process nil))
a0a5c583 2603
bd43c990
RS
2604 (rcirc-print process sender "JOIN" channel "")
2605
2606 ;; print in private chat buffer if it exists
a2524d26 2607 (when (rcirc-get-buffer (rcirc-buffer-process) sender)
a0a5c583 2608 (rcirc-print process sender "JOIN" sender channel))))
bd43c990
RS
2609
2610;; PART and KICK are handled the same way
2611(defun rcirc-handler-PART-or-KICK (process response channel sender nick args)
a2524d26 2612 (rcirc-ignore-update-automatic nick)
bd43c990
RS
2613 (if (not (string= nick (rcirc-nick process)))
2614 ;; this is someone else leaving
a0a5c583
GM
2615 (progn
2616 (rcirc-maybe-remember-nick-quit process nick channel)
2617 (rcirc-remove-nick-channel process nick channel))
adf794e4
EZ
2618 ;; this is us leaving
2619 (mapc (lambda (n)
2620 (rcirc-remove-nick-channel process n channel))
2621 (rcirc-channel-nicks process channel))
2622
2623 ;; if the buffer is still around, make it inactive
2624 (let ((buffer (rcirc-get-buffer process channel)))
2625 (when buffer
195eca78 2626 (rcirc-disconnect-buffer buffer)))))
bd43c990
RS
2627
2628(defun rcirc-handler-PART (process sender args text)
a2524d26
EZ
2629 (let* ((channel (car args))
2630 (reason (cadr args))
2631 (message (concat channel " " reason)))
2632 (rcirc-print process sender "PART" channel message)
2633 ;; print in private chat buffer if it exists
2634 (when (rcirc-get-buffer (rcirc-buffer-process) sender)
2635 (rcirc-print process sender "PART" sender message))
2636
2637 (rcirc-handler-PART-or-KICK process "PART" channel sender sender reason)))
bd43c990
RS
2638
2639(defun rcirc-handler-KICK (process sender args text)
a2524d26
EZ
2640 (let* ((channel (car args))
2641 (nick (cadr args))
2642 (reason (caddr args))
2643 (message (concat nick " " channel " " reason)))
2644 (rcirc-print process sender "KICK" channel message t)
2645 ;; print in private chat buffer if it exists
2646 (when (rcirc-get-buffer (rcirc-buffer-process) nick)
2647 (rcirc-print process sender "KICK" nick message))
2648
2649 (rcirc-handler-PART-or-KICK process "KICK" channel sender nick reason)))
bd43c990 2650
a0a5c583
GM
2651(defun rcirc-maybe-remember-nick-quit (process nick channel)
2652 "Remember NICK as leaving CHANNEL if they recently spoke."
683b7dc6 2653 (let ((elapsed-lines (rcirc-elapsed-lines process nick channel)))
a0a5c583
GM
2654 (when (and elapsed-lines
2655 (< elapsed-lines rcirc-omit-threshold))
2656 (let ((buffer (rcirc-get-buffer process channel)))
2657 (when buffer
2658 (with-current-buffer buffer
683b7dc6
GM
2659 (let ((record (assoc-string nick rcirc-recent-quit-alist t))
2660 (line (rcirc-last-line process nick channel)))
a0a5c583
GM
2661 (if record
2662 (setcdr record line)
2663 (setq rcirc-recent-quit-alist
2664 (cons (cons nick line)
2665 rcirc-recent-quit-alist))))))))))
2666
bd43c990 2667(defun rcirc-handler-QUIT (process sender args text)
db58efbf
EZ
2668 (rcirc-ignore-update-automatic sender)
2669 (mapc (lambda (channel)
a0a5c583
GM
2670 ;; broadcast quit message each channel
2671 (rcirc-print process sender "QUIT" channel (apply 'concat args))
2672 ;; record nick in quit table if they recently spoke
2673 (rcirc-maybe-remember-nick-quit process sender channel))
db58efbf 2674 (rcirc-nick-channels process sender))
db58efbf 2675 (rcirc-nick-remove process sender))
bd43c990
RS
2676
2677(defun rcirc-handler-NICK (process sender args text)
db58efbf 2678 (let* ((old-nick sender)
bd43c990
RS
2679 (new-nick (car args))
2680 (channels (rcirc-nick-channels process old-nick)))
2c8abe90
AS
2681 ;; update list of ignored nicks
2682 (rcirc-ignore-update-automatic old-nick)
2683 (when (member old-nick rcirc-ignore-list)
2684 (add-to-list 'rcirc-ignore-list new-nick)
2685 (add-to-list 'rcirc-ignore-list-automatic new-nick))
bd43c990
RS
2686 ;; print message to nick's channels
2687 (dolist (target channels)
2688 (rcirc-print process sender "NICK" target new-nick))
2689 ;; update private chat buffer, if it exists
adf794e4
EZ
2690 (let ((chat-buffer (rcirc-get-buffer process old-nick)))
2691 (when chat-buffer
2692 (with-current-buffer chat-buffer
2693 (rcirc-print process sender "NICK" old-nick new-nick)
2694 (setq rcirc-target new-nick)
2695 (rename-buffer (rcirc-generate-new-buffer-name process new-nick)))))
bd43c990 2696 ;; remove old nick and add new one
adf794e4 2697 (with-rcirc-process-buffer process
bd43c990
RS
2698 (let ((v (gethash old-nick rcirc-nick-table)))
2699 (remhash old-nick rcirc-nick-table)
2700 (puthash new-nick v rcirc-nick-table))
2701 ;; if this is our nick...
2702 (when (string= old-nick rcirc-nick)
2703 (setq rcirc-nick new-nick)
adf794e4 2704 (rcirc-update-prompt t)
bd43c990
RS
2705 ;; reauthenticate
2706 (when rcirc-auto-authenticate-flag (rcirc-authenticate))))))
2707
2708(defun rcirc-handler-PING (process sender args text)
195eca78 2709 (rcirc-send-string process (concat "PONG :" (car args))))
bd43c990
RS
2710
2711(defun rcirc-handler-PONG (process sender args text)
2712 ;; do nothing
2713 )
2714
2715(defun rcirc-handler-TOPIC (process sender args text)
2716 (let ((topic (cadr args)))
2717 (rcirc-print process sender "TOPIC" (car args) topic)
2718 (with-current-buffer (rcirc-get-buffer process (car args))
2719 (setq rcirc-topic topic))))
2720
a2524d26
EZ
2721(defvar rcirc-nick-away-alist nil)
2722(defun rcirc-handler-301 (process sender args text)
2723 "RPL_AWAY"
2724 (let* ((nick (cadr args))
2725 (rec (assoc-string nick rcirc-nick-away-alist))
2726 (away-message (caddr args)))
2727 (when (or (not rec)
2728 (not (string= (cdr rec) away-message)))
2729 ;; away message has changed
2730 (rcirc-handler-generic process "AWAY" nick (cdr args) text)
2731 (if rec
2732 (setcdr rec away-message)
2733 (setq rcirc-nick-away-alist (cons (cons nick away-message)
2734 rcirc-nick-away-alist))))))
2735
c5aff743
DD
2736(defun rcirc-handler-317 (process sender args text)
2737 "RPL_WHOISIDLE"
2738 (let* ((nick (nth 1 args))
2739 (idle-secs (string-to-number (nth 2 args)))
2740 (idle-string
2741 (if (< idle-secs most-positive-fixnum)
2742 (format-seconds "%yy %dd %hh %mm %z%ss" idle-secs)
2743 "a very long time"))
2744 (signon-time (seconds-to-time (string-to-number (nth 3 args))))
2745 (signon-string (format-time-string "%c" signon-time))
2746 (message (format "%s idle for %s, signed on %s"
2747 nick idle-string signon-string)))
2748 (rcirc-print process sender "317" nil message t)))
2749
bd43c990
RS
2750(defun rcirc-handler-332 (process sender args text)
2751 "RPL_TOPIC"
adf794e4
EZ
2752 (let ((buffer (or (rcirc-get-buffer process (cadr args))
2753 (rcirc-get-temp-buffer-create process (cadr args)))))
2754 (with-current-buffer buffer
2755 (setq rcirc-topic (caddr args)))))
bd43c990
RS
2756
2757(defun rcirc-handler-333 (process sender args text)
027b979c
DD
2758 "333 says who set the topic and when.
2759Not in rfc1459.txt"
adf794e4
EZ
2760 (let ((buffer (or (rcirc-get-buffer process (cadr args))
2761 (rcirc-get-temp-buffer-create process (cadr args)))))
2762 (with-current-buffer buffer
2763 (let ((setter (caddr args))
2764 (time (current-time-string
2765 (seconds-to-time
2766 (string-to-number (cadddr args))))))
2767 (rcirc-print process sender "TOPIC" (cadr args)
2768 (format "%s (%s on %s)" rcirc-topic setter time))))))
bd43c990
RS
2769
2770(defun rcirc-handler-477 (process sender args text)
2771 "ERR_NOCHANMODES"
2772 (rcirc-print process sender "477" (cadr args) (caddr args)))
2773
2774(defun rcirc-handler-MODE (process sender args text)
2775 (let ((target (car args))
2776 (msg (mapconcat 'identity (cdr args) " ")))
2777 (rcirc-print process sender "MODE"
2778 (if (string= target (rcirc-nick process))
2779 nil
2780 target)
2781 msg)
2782
2783 ;; print in private chat buffers if they exist
2784 (mapc (lambda (nick)
db58efbf
EZ
2785 (when (rcirc-get-buffer process nick)
2786 (rcirc-print process sender "MODE" nick msg)))
adf794e4 2787 (cddr args))))
bd43c990
RS
2788
2789(defun rcirc-get-temp-buffer-create (process channel)
2790 "Return a buffer based on PROCESS and CHANNEL."
2791 (let ((tmpnam (concat " " (downcase channel) "TMP" (process-name process))))
2792 (get-buffer-create tmpnam)))
2793
2794(defun rcirc-handler-353 (process sender args text)
2795 "RPL_NAMREPLY"
0ba690bd
DD
2796 (let ((channel (nth 2 args))
2797 (names (or (nth 3 args) "")))
bd43c990
RS
2798 (mapc (lambda (nick)
2799 (rcirc-put-nick-channel process nick channel))
0ba690bd
DD
2800 (split-string names " " t))
2801 ;; create a temporary buffer to insert the names into
2802 ;; rcirc-handler-366 (RPL_ENDOFNAMES) will handle it
bd43c990
RS
2803 (with-current-buffer (rcirc-get-temp-buffer-create process channel)
2804 (goto-char (point-max))
2805 (insert (car (last args)) " "))))
2806
2807(defun rcirc-handler-366 (process sender args text)
2808 "RPL_ENDOFNAMES"
2809 (let* ((channel (cadr args))
2810 (buffer (rcirc-get-temp-buffer-create process channel)))
2811 (with-current-buffer buffer
2812 (rcirc-print process sender "NAMES" channel
c62bf05a 2813 (let ((content (buffer-substring (point-min) (point-max))))
aa1bc616 2814 (rcirc-sort-nicknames-join content " "))))
bd43c990
RS
2815 (kill-buffer buffer)))
2816
2817(defun rcirc-handler-433 (process sender args text)
2818 "ERR_NICKNAMEINUSE"
2819 (rcirc-handler-generic process "433" sender args text)
2820 (let* ((new-nick (concat (cadr args) "`")))
adf794e4 2821 (with-rcirc-process-buffer process
bd43c990
RS
2822 (rcirc-cmd-nick new-nick nil process))))
2823
2824(defun rcirc-authenticate ()
2825 "Send authentication to process associated with current buffer.
db58efbf 2826Passwords are stored in `rcirc-authinfo' (which see)."
bd43c990 2827 (interactive)
a2524d26 2828 (with-rcirc-server-buffer
db58efbf 2829 (dolist (i rcirc-authinfo)
a2524d26
EZ
2830 (let ((process (rcirc-buffer-process))
2831 (server (car i))
db58efbf
EZ
2832 (nick (caddr i))
2833 (method (cadr i))
2834 (args (cdddr i)))
77f63d30
DD
2835 (when (and (string-match server rcirc-server))
2836 (if (and (memq method '(nickserv chanserv bitlbee))
2837 (string-match nick rcirc-nick))
2838 ;; the following methods rely on the user's nickname.
2839 (case method
2840 (nickserv
2841 (rcirc-send-privmsg
2842 process
1be1d1e9 2843 (or (cadr args) "NickServ")
77f63d30
DD
2844 (concat "IDENTIFY " (car args))))
2845 (chanserv
2846 (rcirc-send-privmsg
2847 process
1be1d1e9 2848 "ChanServ"
77f63d30
DD
2849 (format "IDENTIFY %s %s" (car args) (cadr args))))
2850 (bitlbee
2851 (rcirc-send-privmsg
2852 process
1be1d1e9 2853 "&bitlbee"
77f63d30
DD
2854 (concat "IDENTIFY " (car args)))))
2855 ;; quakenet authentication doesn't rely on the user's nickname.
2856 ;; the variable `nick' here represents the Q account name.
2857 (when (eq method 'quakenet)
aebf69c8 2858 (rcirc-send-privmsg
77f63d30
DD
2859 process
2860 "Q@CServe.quakenet.org"
2861 (format "AUTH %s %s" nick (car args))))))))))
adf794e4 2862
bd43c990
RS
2863(defun rcirc-handler-INVITE (process sender args text)
2864 (rcirc-print process sender "INVITE" nil (mapconcat 'identity args " ") t))
2865
2866(defun rcirc-handler-ERROR (process sender args text)
2867 (rcirc-print process sender "ERROR" nil (mapconcat 'identity args " ")))
2868
2869(defun rcirc-handler-CTCP (process target sender text)
2870 (if (string-match "^\\([^ ]+\\) *\\(.*\\)$" text)
2871 (let* ((request (upcase (match-string 1 text)))
2872 (args (match-string 2 text))
bd43c990
RS
2873 (handler (intern-soft (concat "rcirc-handler-ctcp-" request))))
2874 (if (not (fboundp handler))
db58efbf
EZ
2875 (rcirc-print process sender "ERROR" target
2876 (format "%s sent unsupported ctcp: %s" sender text)
adf794e4 2877 t)
bd43c990 2878 (funcall handler process target sender args)
195eca78
SM
2879 (unless (or (string= request "ACTION")
2880 (string= request "KEEPALIVE"))
db58efbf 2881 (rcirc-print process sender "CTCP" target
adf794e4 2882 (format "%s" text) t))))))
bd43c990
RS
2883
2884(defun rcirc-handler-ctcp-VERSION (process target sender args)
2885 (rcirc-send-string process
db58efbf 2886 (concat "NOTICE " sender
adf794e4 2887 " :\C-aVERSION " rcirc-id-string
bd43c990
RS
2888 "\C-a")))
2889
2890(defun rcirc-handler-ctcp-ACTION (process target sender args)
2891 (rcirc-print process sender "ACTION" target args t))
2892
2893(defun rcirc-handler-ctcp-TIME (process target sender args)
2894 (rcirc-send-string process
db58efbf 2895 (concat "NOTICE " sender
bd43c990 2896 " :\C-aTIME " (current-time-string) "\C-a")))
adf794e4
EZ
2897
2898(defun rcirc-handler-CTCP-response (process target sender message)
2899 (rcirc-print process sender "CTCP" nil message t))
bd43c990 2900\f
adf794e4
EZ
2901(defgroup rcirc-faces nil
2902 "Faces for rcirc."
2903 :group 'rcirc
2904 :group 'faces)
2905
ad8121fe 2906(defface rcirc-my-nick ; font-lock-function-name-face
4b56d0fe
CY
2907 '((((class color) (min-colors 88) (background light)) :foreground "Blue1")
2908 (((class color) (min-colors 88) (background dark)) :foreground "LightSkyBlue")
2909 (((class color) (min-colors 16) (background light)) :foreground "Blue")
2910 (((class color) (min-colors 16) (background dark)) :foreground "LightSkyBlue")
2911 (((class color) (min-colors 8)) :foreground "blue" :weight bold)
2912 (t :inverse-video t :weight bold))
2913 "Rcirc face for my messages."
adf794e4 2914 :group 'rcirc-faces)
bd43c990 2915
ad8121fe
EZ
2916(defface rcirc-other-nick ; font-lock-variable-name-face
2917 '((((class grayscale) (background light))
4b56d0fe 2918 :foreground "Gray90" :weight bold :slant italic)
bd43c990 2919 (((class grayscale) (background dark))
4b56d0fe
CY
2920 :foreground "DimGray" :weight bold :slant italic)
2921 (((class color) (min-colors 88) (background light)) :foreground "DarkGoldenrod")
2922 (((class color) (min-colors 88) (background dark)) :foreground "LightGoldenrod")
2923 (((class color) (min-colors 16) (background light)) :foreground "DarkGoldenrod")
2924 (((class color) (min-colors 16) (background dark)) :foreground "LightGoldenrod")
2925 (((class color) (min-colors 8)) :foreground "yellow" :weight light)
2926 (t :weight bold :slant italic))
2927 "Rcirc face for other users' messages."
adf794e4 2928 :group 'rcirc-faces)
bd43c990 2929
02f47e86
MB
2930(defface rcirc-bright-nick
2931 '((((class grayscale) (background light))
4b56d0fe 2932 :foreground "LightGray" :weight bold :underline t)
02f47e86 2933 (((class grayscale) (background dark))
4b56d0fe
CY
2934 :foreground "Gray50" :weight bold :underline t)
2935 (((class color) (min-colors 88) (background light)) :foreground "CadetBlue")
2936 (((class color) (min-colors 88) (background dark)) :foreground "Aquamarine")
2937 (((class color) (min-colors 16) (background light)) :foreground "CadetBlue")
2938 (((class color) (min-colors 16) (background dark)) :foreground "Aquamarine")
2939 (((class color) (min-colors 8)) :foreground "magenta")
2940 (t :weight bold :underline t))
2941 "Rcirc face for nicks matched by `rcirc-bright-nicks'."
02f47e86
MB
2942 :group 'rcirc-faces)
2943
2944(defface rcirc-dim-nick
2945 '((t :inherit default))
4b56d0fe 2946 "Rcirc face for nicks in `rcirc-dim-nicks'."
02f47e86
MB
2947 :group 'rcirc-faces)
2948
ad8121fe
EZ
2949(defface rcirc-server ; font-lock-comment-face
2950 '((((class grayscale) (background light))
4b56d0fe 2951 :foreground "DimGray" :weight bold :slant italic)
bd43c990 2952 (((class grayscale) (background dark))
4b56d0fe 2953 :foreground "LightGray" :weight bold :slant italic)
ad8121fe 2954 (((class color) (min-colors 88) (background light))
4b56d0fe 2955 :foreground "Firebrick")
ad8121fe 2956 (((class color) (min-colors 88) (background dark))
4b56d0fe 2957 :foreground "chocolate1")
ad8121fe 2958 (((class color) (min-colors 16) (background light))
4b56d0fe 2959 :foreground "red")
ad8121fe 2960 (((class color) (min-colors 16) (background dark))
4b56d0fe
CY
2961 :foreground "red1")
2962 (((class color) (min-colors 8) (background light)))
2963 (((class color) (min-colors 8) (background dark)))
2964 (t :weight bold :slant italic))
2965 "Rcirc face for server messages."
adf794e4 2966 :group 'rcirc-faces)
bd43c990 2967
ad8121fe 2968(defface rcirc-server-prefix ; font-lock-comment-delimiter-face
db58efbf 2969 '((default :inherit rcirc-server)
ad8121fe
EZ
2970 (((class grayscale)))
2971 (((class color) (min-colors 16)))
2972 (((class color) (min-colors 8) (background light))
2973 :foreground "red")
2974 (((class color) (min-colors 8) (background dark))
2975 :foreground "red1"))
4b56d0fe 2976 "Rcirc face for server prefixes."
ad8121fe
EZ
2977 :group 'rcirc-faces)
2978
2979(defface rcirc-timestamp
4b56d0fe
CY
2980 '((t :inherit default))
2981 "Rcirc face for timestamps."
ad8121fe
EZ
2982 :group 'rcirc-faces)
2983
2984(defface rcirc-nick-in-message ; font-lock-keyword-face
4b56d0fe
CY
2985 '((((class grayscale) (background light)) :foreground "LightGray" :weight bold)
2986 (((class grayscale) (background dark)) :foreground "DimGray" :weight bold)
2987 (((class color) (min-colors 88) (background light)) :foreground "Purple")
2988 (((class color) (min-colors 88) (background dark)) :foreground "Cyan1")
2989 (((class color) (min-colors 16) (background light)) :foreground "Purple")
2990 (((class color) (min-colors 16) (background dark)) :foreground "Cyan")
2991 (((class color) (min-colors 8)) :foreground "cyan" :weight bold)
2992 (t :weight bold))
2993 "Rcirc face for instances of your nick within messages."
adf794e4 2994 :group 'rcirc-faces)
bd43c990 2995
4b56d0fe
CY
2996(defface rcirc-nick-in-message-full-line '((t :weight bold))
2997 "Rcirc face for emphasizing the entire message when your nick is mentioned."
92c4adc1 2998 :group 'rcirc-faces)
f8db61b2 2999
ad8121fe 3000(defface rcirc-prompt ; comint-highlight-prompt
4b56d0fe
CY
3001 '((((min-colors 88) (background dark)) :foreground "cyan1")
3002 (((background dark)) :foreground "cyan")
3003 (t :foreground "dark blue"))
3004 "Rcirc face for prompts."
adf794e4 3005 :group 'rcirc-faces)
bd43c990 3006
f8db61b2 3007(defface rcirc-track-nick
4b56d0fe
CY
3008 '((((type tty)) :inherit default)
3009 (t :inverse-video t))
3010 "Rcirc face used in the mode-line when your nick is mentioned."
f8db61b2
EZ
3011 :group 'rcirc-faces)
3012
4b56d0fe
CY
3013(defface rcirc-track-keyword '((t :weight bold))
3014 "Rcirc face used in the mode-line when keywords are mentioned."
f8db61b2
EZ
3015 :group 'rcirc-faces)
3016
4b56d0fe
CY
3017(defface rcirc-url '((t :weight bold))
3018 "Rcirc face used to highlight urls."
f8db61b2
EZ
3019 :group 'rcirc-faces)
3020
4b56d0fe
CY
3021(defface rcirc-keyword '((t :inherit highlight))
3022 "Rcirc face used to highlight keywords."
adf794e4 3023 :group 'rcirc-faces)
a2524d26 3024
bd43c990 3025\f
adf794e4 3026;; When using M-x flyspell-mode, only check words after the prompt
bd43c990
RS
3027(put 'rcirc-mode 'flyspell-mode-predicate 'rcirc-looking-at-input)
3028(defun rcirc-looking-at-input ()
3029 "Returns true if point is past the input marker."
3030 (>= (point) rcirc-prompt-end-marker))
3031\f
3032
3033(provide 'rcirc)
e636ae15 3034
bd43c990 3035;;; rcirc.el ends here