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