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