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