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