Merge from emacs--rel--22
[emacs.git] / lisp / erc / erc-goodies.el
blobff065467f842537b2e656145ccc7a2cad0b0762e
1 ;; erc-goodies.el --- Collection of ERC modules
3 ;; Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008
4 ;; Free Software Foundation, Inc.
6 ;; Author: Jorgen Schaefer <forcer@forcix.cx>
8 ;; Most code is taken verbatim from erc.el, see there for the original
9 ;; authors.
11 ;; This file is part of GNU Emacs.
13 ;; GNU Emacs is free software; you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation; either version 3, or (at your option)
16 ;; any later version.
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs; see the file COPYING. If not, write to the
25 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 ;; Boston, MA 02110-1301, USA.
28 ;;; Commentary:
30 ;; This provides some small but still useful modes for ERC.
32 ;;; Code:
34 (require 'erc)
36 ;;; Imenu support
38 (defun erc-imenu-setup ()
39 "Setup Imenu support in an ERC buffer."
40 (set (make-local-variable 'imenu-create-index-function)
41 'erc-create-imenu-index))
43 (add-hook 'erc-mode-hook 'erc-imenu-setup)
44 (autoload 'erc-create-imenu-index "erc-imenu" "Imenu index creation function")
46 ;;; Automatically scroll to bottom
47 (defcustom erc-input-line-position nil
48 "Specify where to position the input line when using `erc-scroll-to-bottom'.
50 This should be an integer specifying the line of the buffer on which
51 the input line should stay. A value of \"-1\" would keep the input
52 line positioned on the last line in the buffer. This is passed as an
53 argument to `recenter'."
54 :group 'erc-display
55 :type '(choice integer (const nil)))
57 (define-erc-module scrolltobottom nil
58 "This mode causes the prompt to stay at the end of the window."
59 ((add-hook 'erc-mode-hook 'erc-add-scroll-to-bottom)
60 (dolist (buffer (erc-buffer-list))
61 (with-current-buffer buffer
62 (erc-add-scroll-to-bottom))))
63 ((remove-hook 'erc-mode-hook 'erc-add-scroll-to-bottom)
64 (dolist (buffer (erc-buffer-list))
65 (with-current-buffer buffer
66 (remove-hook 'window-scroll-functions 'erc-scroll-to-bottom t)))))
68 (defun erc-add-scroll-to-bottom ()
69 "A hook function for `erc-mode-hook' to recenter output at bottom of window.
71 If you find that ERC hangs when using this function, try customizing
72 the value of `erc-input-line-position'.
74 This works whenever scrolling happens, so it's added to
75 `window-scroll-functions' rather than `erc-insert-post-hook'."
76 ;;(make-local-hook 'window-scroll-functions)
77 (add-hook 'window-scroll-functions 'erc-scroll-to-bottom nil t))
79 (defun erc-scroll-to-bottom (window display-start)
80 "Recenter WINDOW so that `point' is on the last line.
82 This is added to `window-scroll-functions' by `erc-add-scroll-to-bottom'.
84 You can control which line is recentered to by customizing the
85 variable `erc-input-line-position'.
87 DISPLAY-START is ignored."
88 (if (window-live-p window)
89 ;; Temporarily bind resize-mini-windows to nil so that users who have it
90 ;; set to a non-nil value will not suffer from premature minibuffer
91 ;; shrinkage due to the below recenter call. I have no idea why this
92 ;; works, but it solves the problem, and has no negative side effects.
93 ;; (Fran Litterio, 2003/01/07)
94 (let ((resize-mini-windows nil))
95 (erc-with-selected-window window
96 (save-restriction
97 (widen)
98 (when (and erc-insert-marker
99 ;; we're editing a line. Scroll.
100 (> (point) erc-insert-marker))
101 (save-excursion
102 (goto-char (point-max))
103 (recenter (or erc-input-line-position -1))
104 (sit-for 0))))))))
106 ;;; Make read only
107 (define-erc-module readonly nil
108 "This mode causes all inserted text to be read-only."
109 ((add-hook 'erc-insert-post-hook 'erc-make-read-only)
110 (add-hook 'erc-send-post-hook 'erc-make-read-only))
111 ((remove-hook 'erc-insert-post-hook 'erc-make-read-only)
112 (remove-hook 'erc-send-post-hook 'erc-make-read-only)))
114 (defun erc-make-read-only ()
115 "Make all the text in the current buffer read-only.
116 Put this function on `erc-insert-post-hook' and/or `erc-send-post-hook'."
117 (put-text-property (point-min) (point-max) 'read-only t)
118 (put-text-property (point-min) (point-max) 'front-sticky t)
119 (put-text-property (point-min) (point-max) 'rear-nonsticky t))
121 ;;; Move to prompt when typing text
122 (define-erc-module move-to-prompt nil
123 "This mode causes the point to be moved to the prompt when typing text."
124 ((add-hook 'erc-mode-hook 'erc-move-to-prompt-setup)
125 (dolist (buffer (erc-buffer-list))
126 (with-current-buffer buffer
127 (erc-move-to-prompt-setup))))
128 ((remove-hook 'erc-mode-hook 'erc-move-to-prompt-setup)
129 (dolist (buffer (erc-buffer-list))
130 (with-current-buffer buffer
131 (remove-hook 'pre-command-hook 'erc-move-to-prompt t)))))
133 (defun erc-move-to-prompt ()
134 "Move the point to the ERC prompt if this is a self-inserting command."
135 (when (and erc-input-marker (< (point) erc-input-marker)
136 (eq 'self-insert-command this-command))
137 (deactivate-mark)
138 (push-mark)
139 (goto-char (point-max))))
141 (defun erc-move-to-prompt-setup ()
142 "Initialize the move-to-prompt module for XEmacs."
143 (add-hook 'pre-command-hook 'erc-move-to-prompt nil t))
145 ;;; Keep place in unvisited channels
146 (define-erc-module keep-place nil
147 "Leave point above un-viewed text in other channels."
148 ((add-hook 'erc-insert-pre-hook 'erc-keep-place))
149 ((remove-hook 'erc-insert-pre-hook 'erc-keep-place)))
151 (defun erc-keep-place (ignored)
152 "Move point away from the last line in a non-selected ERC buffer."
153 (when (and (not (eq (window-buffer (selected-window))
154 (current-buffer)))
155 (>= (point) erc-insert-marker))
156 (deactivate-mark)
157 (goto-char (erc-beg-of-input-line))
158 (forward-line -1)))
160 ;;; Distinguish non-commands
161 (defvar erc-noncommands-list '(erc-cmd-ME
162 erc-cmd-COUNTRY
163 erc-cmd-SV
164 erc-cmd-SM
165 erc-cmd-SMV
166 erc-cmd-LASTLOG)
167 "List of commands that are aliases for CTCP ACTION or for erc messages.
169 If a command's function symbol is in this list, the typed command
170 does not appear in the ERC buffer after the user presses ENTER.")
172 (define-erc-module noncommands nil
173 "This mode distinguishies non-commands.
174 Commands listed in `erc-insert-this' know how to display
175 themselves."
176 ((add-hook 'erc-send-pre-hook 'erc-send-distinguish-noncommands))
177 ((remove-hook 'erc-send-pre-hook 'erc-send-distinguish-noncommands)))
179 (defun erc-send-distinguish-noncommands (str)
180 "If STR is an ERC non-command, set `erc-insert-this' to nil."
181 (let* ((command (erc-extract-command-from-line str))
182 (cmd-fun (and command
183 (car command))))
184 (when (and cmd-fun
185 (not (string-match "\n.+$" str))
186 (memq cmd-fun erc-noncommands-list))
187 (setq erc-insert-this nil))))
189 ;;; IRC control character processing.
190 (defgroup erc-control-characters nil
191 "Dealing with control characters"
192 :group 'erc)
194 (defcustom erc-interpret-controls-p t
195 "*If non-nil, display IRC colours and other highlighting effects.
197 If this is set to the symbol `remove', ERC removes all IRC colors and
198 highlighting effects. When this variable is non-nil, it can cause Emacs to run
199 slowly on systems lacking sufficient CPU speed. In chatty channels, or in an
200 emergency (message flood) it can be turned off to save processing time. See
201 `erc-toggle-interpret-controls'."
202 :group 'erc-control-characters
203 :type '(choice (const :tag "Highlight control characters" t)
204 (const :tag "Remove control characters" remove)
205 (const :tag "Display raw control characters" nil)))
207 (defcustom erc-interpret-mirc-color nil
208 "*If non-nil, erc will interpret mIRC color codes."
209 :group 'erc-control-characters
210 :type 'boolean)
212 (defcustom erc-beep-p nil
213 "Beep if C-g is in the server message.
214 The value `erc-interpret-controls-p' must also be t for this to work."
215 :group 'erc-control-characters
216 :type 'boolean)
218 (defface erc-bold-face '((t (:bold t)))
219 "ERC bold face."
220 :group 'erc-faces)
221 (defface erc-inverse-face
222 '((t (:foreground "White" :background "Black")))
223 "ERC inverse face."
224 :group 'erc-faces)
225 (defface erc-underline-face '((t (:underline t)))
226 "ERC underline face."
227 :group 'erc-faces)
229 (defface fg:erc-color-face0 '((t (:foreground "White")))
230 "ERC face."
231 :group 'erc-faces)
232 (defface fg:erc-color-face1 '((t (:foreground "black")))
233 "ERC face."
234 :group 'erc-faces)
235 (defface fg:erc-color-face2 '((t (:foreground "blue4")))
236 "ERC face."
237 :group 'erc-faces)
238 (defface fg:erc-color-face3 '((t (:foreground "green4")))
239 "ERC face."
240 :group 'erc-faces)
241 (defface fg:erc-color-face4 '((t (:foreground "red")))
242 "ERC face."
243 :group 'erc-faces)
244 (defface fg:erc-color-face5 '((t (:foreground "brown")))
245 "ERC face."
246 :group 'erc-faces)
247 (defface fg:erc-color-face6 '((t (:foreground "purple")))
248 "ERC face."
249 :group 'erc-faces)
250 (defface fg:erc-color-face7 '((t (:foreground "orange")))
251 "ERC face."
252 :group 'erc-faces)
253 (defface fg:erc-color-face8 '((t (:foreground "yellow")))
254 "ERC face."
255 :group 'erc-faces)
256 (defface fg:erc-color-face9 '((t (:foreground "green")))
257 "ERC face."
258 :group 'erc-faces)
259 (defface fg:erc-color-face10 '((t (:foreground "lightblue1")))
260 "ERC face."
261 :group 'erc-faces)
262 (defface fg:erc-color-face11 '((t (:foreground "cyan")))
263 "ERC face."
264 :group 'erc-faces)
265 (defface fg:erc-color-face12 '((t (:foreground "blue")))
266 "ERC face."
267 :group 'erc-faces)
268 (defface fg:erc-color-face13 '((t (:foreground "deeppink")))
269 "ERC face."
270 :group 'erc-faces)
271 (defface fg:erc-color-face14 '((t (:foreground "gray50")))
272 "ERC face."
273 :group 'erc-faces)
274 (defface fg:erc-color-face15 '((t (:foreground "gray90")))
275 "ERC face."
276 :group 'erc-faces)
278 (defface bg:erc-color-face0 '((t (:background "White")))
279 "ERC face."
280 :group 'erc-faces)
281 (defface bg:erc-color-face1 '((t (:background "black")))
282 "ERC face."
283 :group 'erc-faces)
284 (defface bg:erc-color-face2 '((t (:background "blue4")))
285 "ERC face."
286 :group 'erc-faces)
287 (defface bg:erc-color-face3 '((t (:background "green4")))
288 "ERC face."
289 :group 'erc-faces)
290 (defface bg:erc-color-face4 '((t (:background "red")))
291 "ERC face."
292 :group 'erc-faces)
293 (defface bg:erc-color-face5 '((t (:background "brown")))
294 "ERC face."
295 :group 'erc-faces)
296 (defface bg:erc-color-face6 '((t (:background "purple")))
297 "ERC face."
298 :group 'erc-faces)
299 (defface bg:erc-color-face7 '((t (:background "orange")))
300 "ERC face."
301 :group 'erc-faces)
302 (defface bg:erc-color-face8 '((t (:background "yellow")))
303 "ERC face."
304 :group 'erc-faces)
305 (defface bg:erc-color-face9 '((t (:background "green")))
306 "ERC face."
307 :group 'erc-faces)
308 (defface bg:erc-color-face10 '((t (:background "lightblue1")))
309 "ERC face."
310 :group 'erc-faces)
311 (defface bg:erc-color-face11 '((t (:background "cyan")))
312 "ERC face."
313 :group 'erc-faces)
314 (defface bg:erc-color-face12 '((t (:background "blue")))
315 "ERC face."
316 :group 'erc-faces)
317 (defface bg:erc-color-face13 '((t (:background "deeppink")))
318 "ERC face."
319 :group 'erc-faces)
320 (defface bg:erc-color-face14 '((t (:background "gray50")))
321 "ERC face."
322 :group 'erc-faces)
323 (defface bg:erc-color-face15 '((t (:background "gray90")))
324 "ERC face."
325 :group 'erc-faces)
327 (defun erc-get-bg-color-face (n)
328 "Fetches the right face for background color N (0-15)."
329 (if (stringp n) (setq n (string-to-number n)))
330 (if (not (numberp n))
331 (prog1 'default
332 (erc-error "erc-get-bg-color-face: n is NaN: %S" n))
333 (when (> n 16)
334 (erc-log (format " Wrong color: %s" n))
335 (setq n (mod n 16)))
336 (cond
337 ((and (>= n 0) (< n 16))
338 (intern (concat "bg:erc-color-face" (number-to-string n))))
339 (t (erc-log (format " Wrong color: %s" n)) 'default))))
341 (defun erc-get-fg-color-face (n)
342 "Fetches the right face for foreground color N (0-15)."
343 (if (stringp n) (setq n (string-to-number n)))
344 (if (not (numberp n))
345 (prog1 'default
346 (erc-error "erc-get-fg-color-face: n is NaN: %S" n))
347 (when (> n 16)
348 (erc-log (format " Wrong color: %s" n))
349 (setq n (mod n 16)))
350 (cond
351 ((and (>= n 0) (< n 16))
352 (intern (concat "fg:erc-color-face" (number-to-string n))))
353 (t (erc-log (format " Wrong color: %s" n)) 'default))))
355 (define-erc-module irccontrols nil
356 "This mode enables the interpretation of IRC control chars."
357 ((add-hook 'erc-insert-modify-hook 'erc-controls-highlight)
358 (add-hook 'erc-send-modify-hook 'erc-controls-highlight))
359 ((remove-hook 'erc-insert-modify-hook 'erc-controls-highlight)
360 (remove-hook 'erc-send-modify-hook 'erc-controls-highlight)))
362 (defun erc-controls-interpret (str)
363 "Return a copy of STR after dealing with IRC control characters.
364 See `erc-interpret-controls-p' and `erc-interpret-mirc-color' for options."
365 (when str
366 (let ((s str))
367 (cond ((eq erc-interpret-controls-p 'remove)
368 (erc-controls-strip s))
369 (erc-interpret-controls-p
370 (let ((boldp nil)
371 (inversep nil)
372 (underlinep nil)
373 (fg nil)
374 (bg nil))
375 (while (string-match erc-controls-highlight-regexp s)
376 (let ((control (match-string 1 s))
377 (fg-color (match-string 2 s))
378 (bg-color (match-string 4 s))
379 (start (match-beginning 0))
380 (end (+ (match-beginning 0)
381 (length (match-string 5 s)))))
382 (setq s (erc-replace-match-subexpression-in-string
383 "" s control 1 start))
384 (cond ((and erc-interpret-mirc-color (or fg-color bg-color))
385 (setq fg fg-color)
386 (setq bg bg-color))
387 ((string= control "\C-b")
388 (setq boldp (not boldp)))
389 ((string= control "\C-v")
390 (setq inversep (not inversep)))
391 ((string= control "\C-_")
392 (setq underlinep (not underlinep)))
393 ((string= control "\C-c")
394 (setq fg nil
395 bg nil))
396 ((string= control "\C-g")
397 (when erc-beep-p
398 (ding)))
399 ((string= control "\C-o")
400 (setq boldp nil
401 inversep nil
402 underlinep nil
403 fg nil
404 bg nil))
405 (t nil))
406 (erc-controls-propertize
407 start end boldp inversep underlinep fg bg s)))
409 (t s)))))
411 (defun erc-controls-strip (str)
412 "Return a copy of STR with all IRC control characters removed."
413 (when str
414 (let ((s str))
415 (while (string-match erc-controls-remove-regexp s)
416 (setq s (replace-match "" nil nil s)))
417 s)))
419 (defvar erc-controls-remove-regexp
420 "\C-b\\|\C-_\\|\C-v\\|\C-g\\|\C-o\\|\C-c[0-9]?[0-9]?\\(,[0-9][0-9]?\\)?"
421 "Regular expression which matches control characters to remove.")
423 (defvar erc-controls-highlight-regexp
424 (concat "\\(\C-b\\|\C-v\\|\C-_\\|\C-g\\|\C-o\\|"
425 "\C-c\\([0-9][0-9]?\\)?\\(,\\([0-9][0-9]?\\)\\)?\\)"
426 "\\([^\C-b\C-v\C-_\C-c\C-g\C-o\n]*\\)")
427 "Regular expression which matches control chars and the text to highlight.")
429 (defun erc-controls-highlight ()
430 "Highlight IRC control chars in the buffer.
431 This is useful for `erc-insert-modify-hook' and
432 `erc-send-modify-hook'. Also see `erc-interpret-controls-p' and
433 `erc-interpret-mirc-color'."
434 (goto-char (point-min))
435 (cond ((eq erc-interpret-controls-p 'remove)
436 (while (re-search-forward erc-controls-remove-regexp nil t)
437 (replace-match "")))
438 (erc-interpret-controls-p
439 (let ((boldp nil)
440 (inversep nil)
441 (underlinep nil)
442 (fg nil)
443 (bg nil))
444 (while (re-search-forward erc-controls-highlight-regexp nil t)
445 (let ((control (match-string 1))
446 (fg-color (match-string 2))
447 (bg-color (match-string 4))
448 (start (match-beginning 0))
449 (end (+ (match-beginning 0) (length (match-string 5)))))
450 (replace-match "" nil nil nil 1)
451 (cond ((and erc-interpret-mirc-color (or fg-color bg-color))
452 (setq fg fg-color)
453 (setq bg bg-color))
454 ((string= control "\C-b")
455 (setq boldp (not boldp)))
456 ((string= control "\C-v")
457 (setq inversep (not inversep)))
458 ((string= control "\C-_")
459 (setq underlinep (not underlinep)))
460 ((string= control "\C-c")
461 (setq fg nil
462 bg nil))
463 ((string= control "\C-g")
464 (when erc-beep-p
465 (ding)))
466 ((string= control "\C-o")
467 (setq boldp nil
468 inversep nil
469 underlinep nil
470 fg nil
471 bg nil))
472 (t nil))
473 (erc-controls-propertize start end
474 boldp inversep underlinep fg bg)))))
475 (t nil)))
477 (defun erc-controls-propertize (from to boldp inversep underlinep fg bg
478 &optional str)
479 "Prepend properties from IRC control characters between FROM and TO.
480 If optional argument STR is provided, apply to STR, otherwise prepend properties
481 to a region in the current buffer."
482 (font-lock-prepend-text-property
483 from
485 'face
486 (append (if boldp
487 '(erc-bold-face)
488 nil)
489 (if inversep
490 '(erc-inverse-face)
491 nil)
492 (if underlinep
493 '(erc-underline-face)
494 nil)
495 (if fg
496 (list (erc-get-fg-color-face fg))
497 nil)
498 (if bg
499 (list (erc-get-bg-color-face bg))
500 nil))
501 str)
502 str)
504 (defun erc-toggle-interpret-controls (&optional arg)
505 "Toggle interpretation of control sequences in messages.
507 If ARG is positive, interpretation is turned on.
508 Else interpretation is turned off."
509 (interactive "P")
510 (cond ((and (numberp arg) (> arg 0))
511 (setq erc-interpret-controls-p t))
512 (arg (setq erc-interpret-controls-p nil))
513 (t (setq erc-interpret-controls-p (not erc-interpret-controls-p))))
514 (message "ERC color interpretation %s"
515 (if erc-interpret-controls-p "ON" "OFF")))
517 ;; Smiley
518 (define-erc-module smiley nil
519 "This mode translates text-smileys such as :-) into pictures.
520 This requires the function `smiley-region', which is defined in
521 smiley.el, which is part of Gnus."
522 ((add-hook 'erc-insert-modify-hook 'erc-smiley)
523 (add-hook 'erc-send-modify-hook 'erc-smiley))
524 ((remove-hook 'erc-insert-modify-hook 'erc-smiley)
525 (remove-hook 'erc-send-modify-hook 'erc-smiley)))
527 (defun erc-smiley ()
528 "Smilify a region.
529 This function should be used with `erc-insert-modify-hook'."
530 (when (fboundp 'smiley-region)
531 (smiley-region (point-min) (point-max))))
533 ;; Unmorse
534 (define-erc-module unmorse nil
535 "This mode causes morse code in the current channel to be unmorsed."
536 ((add-hook 'erc-insert-modify-hook 'erc-unmorse))
537 ((remove-hook 'erc-insert-modify-hook 'erc-unmorse)))
539 (defun erc-unmorse ()
540 "Unmorse some text.
541 Add this to `erc-insert-modify-hook' if you happen to be on a
542 channel that has weird people talking in morse to each other.
544 See also `unmorse-region'."
545 (goto-char (point-min))
546 (when (re-search-forward "[.-]+\\([.-]*/? *\\)+[.-]+/?" nil t)
547 (save-restriction
548 (narrow-to-region (match-beginning 0) (match-end 0))
549 ;; Turn " / " into " "
550 (goto-char (point-min))
551 (while (re-search-forward " / " nil t)
552 (replace-match " "))
553 ;; Turn "/ " into "/"
554 (goto-char (point-min))
555 (while (re-search-forward "/ " nil t)
556 (replace-match "/"))
557 ;; Unmorse region
558 (unmorse-region (point-min) (point-max)))))
560 ;;; erc-occur
561 (defun erc-occur (string &optional proc)
562 "Search for STRING in all buffers related to current server.
563 If called interactively and prefix argument is given, search on all connected
564 servers. If called from a program, PROC specifies the server process."
565 (interactive
566 (list (read-string "Search for: ")
567 (if current-prefix-arg
568 nil erc-server-process)))
569 (if (fboundp 'multi-occur)
570 (multi-occur (erc-buffer-list nil proc) string)
571 (error "`multi-occur' is not defined as a function")))
573 (provide 'erc-goodies)
575 ;; arch-tag: d987ae26-9e28-4c72-9596-e617309fb582
576 ;;; erc-goodies.el ends here