Initial Commit
[packages] / xemacs-packages / erc / erc-match.el
1 ;;; erc-match.el --- Highlight messages matching certain regexps
2
3 ;; Copyright (C) 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
4
5 ;; Author: Andreas Fuchs <asf@void.at>
6 ;; Keywords: comm, faces
7 ;; URL: http://www.emacswiki.org/cgi-bin/wiki.pl?ErcMatch
8
9 ;; This file is part of GNU Emacs.
10
11 ;; GNU Emacs is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; GNU Emacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 ;; GNU General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with GNU Emacs; see the file COPYING.  If not, write to the
23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301, USA.
25
26 ;;; Commentary:
27
28 ;; This file includes stuff to work with pattern matching in ERC. If
29 ;; you were used to customizing erc-fools, erc-keywords, erc-pals,
30 ;; erc-dangerous-hosts and the like, this file contains these
31 ;; customizable variables.
32
33 ;; Usage:
34 ;; Put (erc-match-mode 1) into your ~/.emacs file.
35
36 ;;; Code:
37
38 (require 'erc)
39 (eval-when-compile (require 'cl))
40
41 ;; Customisation:
42
43 (defconst erc-match-version "$Revision: 1.51.2.1 $"
44   "ERC match mode revision.")
45
46 (defgroup erc-match nil
47   "Keyword and Friend/Foe/... recognition.
48 Group containing all things concerning pattern matching in ERC
49 messages."
50   :group 'erc)
51
52 ;;;###autoload (autoload 'erc-match-mode "erc-match")
53 (define-erc-module match nil
54   "This mode checks whether messages match certain patterns.  If so,
55 they are hidden or highlighted.  This is controlled via the variables
56 `erc-pals', `erc-fools', `erc-keywords', `erc-dangerous-hosts', and
57 `erc-current-nick-highlight-type'.  For all these highlighting types,
58 you can decide whether the entire message or only the sending nick is
59 highlighted."
60   ((add-hook 'erc-insert-modify-hook 'erc-match-message 'append))
61   ((remove-hook 'erc-insert-modify-hook 'erc-match-message)))
62
63 ;; Remaining customizations
64
65 (defcustom erc-pals nil
66   "List of pals on IRC."
67   :group 'erc-match
68   :type '(repeat regexp))
69
70 (defcustom erc-fools nil
71   "List of fools on IRC."
72   :group 'erc-match
73   :type '(repeat regexp))
74
75 (defcustom erc-keywords nil
76   "List of keywords to highlight in all incoming messages.
77 Each entry in the list is either a regexp, or a cons cell with the
78 regexp in the car and the face to use in the cdr.  If no face is
79 specified, `erc-keyword-face' is used."
80   :group 'erc-match
81   :type '(repeat (choice regexp
82                          (list regexp face))))
83
84 (defcustom erc-dangerous-hosts nil
85   "List of regexps for hosts to highlight.
86 Useful to mark nicks from dangerous hosts."
87   :group 'erc-match
88   :type '(repeat regexp))
89
90 (defcustom erc-current-nick-highlight-type 'keyword
91   "*Determines how to highlight text in which your current nickname appears
92 \(does not apply to text sent by you\).
93
94 The following values are allowed:
95
96  nil              - do not highlight the message at all
97  'keyword         - highlight all instances of current nickname in message
98  'nick            - highlight the nick of the user who typed your nickname
99  'nick-or-keyword - highlight the nick of the user who typed your nickname,
100                     or all instances of the current nickname if there was
101                     no sending user
102  'all             - highlight the entire message where current nickname occurs
103
104 Any other value disables highlighting of current nickname altogether."
105   :group 'erc-match
106   :type '(choice (const nil)
107                  (const nick)
108                  (const keyword)
109                  (const nick-or-keyword)
110                  (const all)))
111
112 (defcustom erc-pal-highlight-type 'nick
113   "*Determines how to highlight messages by pals.
114 See `erc-pals'.
115
116 The following values are allowed:
117
118     nil   - do not highlight the message at all
119     'nick - highlight pal's nickname only
120     'all  - highlight the entire message from pal
121
122 Any other value disables pal highlighting altogether."
123   :group 'erc-match
124   :type '(choice (const nil)
125                  (const nick)
126                  (const all)))
127
128 (defcustom erc-fool-highlight-type 'nick
129   "*Determines how to highlight messages by fools.
130 See `erc-fools'.
131
132 The following values are allowed:
133
134     nil   - do not highlight the message at all
135     'nick - highlight fool's nickname only
136     'all  - highlight the entire message from fool
137
138 Any other value disables fool highlighting altogether."
139   :group 'erc-match
140   :type '(choice (const nil)
141                  (const nick)
142                  (const all)))
143
144 (defcustom erc-keyword-highlight-type 'keyword
145   "*Determines how to highlight messages containing keywords.
146 See variable `erc-keywords'.
147
148 The following values are allowed:
149
150     'keyword - highlight keyword only
151     'all     - highlight the entire message containing keyword
152
153 Any other value disables keyword highlighting altogether."
154   :group 'erc-match
155   :type '(choice (const nil)
156                  (const keyword)
157                  (const all)))
158
159 (defcustom erc-dangerous-host-highlight-type 'nick
160   "*Determines how to highlight messages by nicks from dangerous-hosts.
161 See `erc-dangerous-hosts'.
162
163 The following values are allowed:
164
165     'nick - highlight nick from dangerous-host only
166     'all  - highlight the entire message from dangerous-host
167
168 Any other value disables dangerous-host highlighting altogether."
169   :group 'erc-match
170   :type '(choice (const nil)
171                  (const nick)
172                  (const all)))
173
174
175 (defcustom erc-log-matches-types-alist '((keyword . "ERC Keywords"))
176   "Alist telling ERC where to log which match types.
177 Valid match type keys are:
178 - keyword
179 - pal
180 - dangerous-host
181 - fool
182 - current-nick
183
184 The other element of each cons pair in this list is the buffer name to
185 use for the logged message."
186   :group 'erc-match
187   :type '(repeat (cons (choice :tag "Key"
188                                (const keyword)
189                                (const pal)
190                                (const dangerous-host)
191                                (const fool)
192                                (const current-nick))
193                        (string :tag "Buffer name"))))
194
195 (defcustom erc-log-matches-flag 'away
196   "Flag specifying when matched message logging should happen.
197 When nil, don't log any matched messages.
198 When t, log messages.
199 When 'away, log messages only when away."
200   :group 'erc-match
201   :type '(choice (const nil)
202                  (const away)
203                  (const t)))
204
205 (defcustom erc-log-match-format "%t<%n:%c> %m"
206   "Format for matched Messages.
207 This variable specifies how messages in the corresponding log buffers will
208 be formatted. The various format specs are:
209
210 %t Timestamp (uses `erc-timestamp-format' if non-nil or \"[%Y-%m-%d %H:%M] \")
211 %n Nickname of sender
212 %u Nickname!user@host of sender
213 %c Channel in which this was received
214 %m Message"
215   :group 'erc-match
216   :type 'string)
217
218 (defcustom erc-beep-match-types '(current-nick)
219   "Types of matches to beep for when a match occurs.
220 The function `erc-beep-on-match' needs to be added to `erc-text-matched-hook'
221 for beeping to work."
222   :group 'erc-match
223   :type '(choice (repeat :tag "Beep on match" (choice
224                                                (const current-nick)
225                                                (const keyword)
226                                                (const pal)
227                                                (const dangerous-host)
228                                                (const fool)))
229                  (const :tag "Don't beep" nil)))
230
231 (defcustom erc-text-matched-hook '(erc-log-matches)
232   "Hook run when text matches a given match-type.
233 Functions in this hook are passed as arguments:
234 \(match-type nick!user@host message) where MATCH-TYPE is a symbol of:
235 current-nick, keyword, pal, dangerous-host, fool"
236   :options '(erc-log-matches erc-hide-fools erc-beep-on-match)
237   :group 'erc-match
238   :type 'hook)
239
240 ;; Internal variables:
241
242 ;; This is exactly the same as erc-button-syntax-table.  Should we
243 ;; just put it in erc.el
244 (defvar erc-match-syntax-table
245   (let ((table (make-syntax-table)))
246     (modify-syntax-entry ?\( "w" table)
247     (modify-syntax-entry ?\) "w" table)
248     (modify-syntax-entry ?\[ "w" table)
249     (modify-syntax-entry ?\] "w" table)
250     (modify-syntax-entry ?\{ "w" table)
251     (modify-syntax-entry ?\} "w" table)
252     (modify-syntax-entry ?` "w" table)
253     (modify-syntax-entry ?' "w" table)
254     (modify-syntax-entry ?^ "w" table)
255     (modify-syntax-entry ?- "w" table)
256     (modify-syntax-entry ?_ "w" table)
257     (modify-syntax-entry ?| "w" table)
258     (modify-syntax-entry ?\\ "w" table)
259     table)
260   "Syntax table used when highlighting messages.
261 This syntax table should make all the legal nick characters word
262 constituents.")
263
264 ;; Faces:
265
266 (defface erc-current-nick-face '((t (:bold t :foreground "DarkTurquoise")))
267   "ERC face for occurrences of your current nickname."
268   :group 'erc-faces)
269
270 (defface erc-dangerous-host-face '((t (:foreground "red")))
271   "ERC face for people on dangerous hosts.
272 See `erc-dangerous-hosts'."
273   :group 'erc-faces)
274
275 (defface erc-pal-face '((t (:bold t :foreground "Magenta")))
276   "ERC face for your pals.
277 See `erc-pals'."
278   :group 'erc-faces)
279
280 (defface erc-fool-face '((t (:foreground "dim gray")))
281   "ERC face for fools on the channel.
282 See `erc-fools'."
283   :group 'erc-faces)
284
285 (defface erc-keyword-face '((t (:bold t :foreground "pale green")))
286   "ERC face for your keywords.
287 Note that this is the default face to use if
288 `erc-keywords' does not specify another."
289   :group 'erc-faces)
290
291 ;; Functions:
292
293 (defun erc-add-entry-to-list (list prompt &optional completions)
294   "Add an entry interactively to a list.
295 LIST must be passed as a symbol
296 The query happens using PROMPT.
297 Completion is performed on the optional alist COMPLETIONS."
298   (let ((entry (completing-read
299                 prompt
300                 completions
301                 (lambda (x)
302                   (not (erc-member-ignore-case (car x) (symbol-value list)))))))
303     (if (erc-member-ignore-case entry (symbol-value list))
304         (error (format "\"%s\" is already on the list" entry))
305       (set list (cons entry (symbol-value list))))))
306
307 (defun erc-remove-entry-from-list (list prompt)
308   "Remove an entry interactively from a list.
309 LIST must be passed as a symbol.
310 The elements of LIST can be strings, or cons cells where the
311 car is the string."
312   (let* ((alist (mapcar (lambda (x)
313                           (if (listp x)
314                               x
315                             (list x)))
316                         (symbol-value list)))
317          (entry (completing-read
318                  prompt
319                  alist
320                  nil
321                  t)))
322     (if (erc-member-ignore-case entry (symbol-value list))
323         ;; plain string
324         (set list (delete entry (symbol-value list)))
325       ;; cons cell
326       (set list (delete (assoc entry (symbol-value list))
327                         (symbol-value list))))))
328
329 ;;;###autoload
330 (defun erc-add-pal ()
331   "Add pal interactively to `erc-pals'."
332   (interactive)
333   (erc-add-entry-to-list 'erc-pals "Add pal: " (erc-get-server-nickname-alist)))
334
335 ;;;###autoload
336 (defun erc-delete-pal ()
337   "Delete pal interactively to `erc-pals'."
338   (interactive)
339   (erc-remove-entry-from-list 'erc-pals "Delete pal: "))
340
341 ;;;###autoload
342 (defun erc-add-fool ()
343   "Add fool interactively to `erc-fools'."
344   (interactive)
345   (erc-add-entry-to-list 'erc-fools "Add fool: "
346                          (erc-get-server-nickname-alist)))
347
348 ;;;###autoload
349 (defun erc-delete-fool ()
350   "Delete fool interactively to `erc-fools'."
351   (interactive)
352   (erc-remove-entry-from-list 'erc-fools "Delete fool: "))
353
354 ;;;###autoload
355 (defun erc-add-keyword ()
356   "Add keyword interactively to `erc-keywords'."
357   (interactive)
358   (erc-add-entry-to-list 'erc-keywords "Add keyword: "))
359
360 ;;;###autoload
361 (defun erc-delete-keyword ()
362   "Delete keyword interactively to `erc-keywords'."
363   (interactive)
364   (erc-remove-entry-from-list 'erc-keywords "Delete keyword: "))
365
366 ;;;###autoload
367 (defun erc-add-dangerous-host ()
368   "Add dangerous-host interactively to `erc-dangerous-hosts'."
369   (interactive)
370   (erc-add-entry-to-list 'erc-dangerous-hosts "Add dangerous-host: "))
371
372 ;;;###autoload
373 (defun erc-delete-dangerous-host ()
374   "Delete dangerous-host interactively to `erc-dangerous-hosts'."
375   (interactive)
376   (erc-remove-entry-from-list 'erc-dangerous-hosts "Delete dangerous-host: "))
377
378 (defun erc-match-current-nick-p (nickuserhost msg)
379   "Check whether the current nickname is in MSG.
380 NICKUSERHOST will be ignored."
381   (with-syntax-table erc-match-syntax-table
382     (and msg
383          (string-match (concat "\\b"
384                                (regexp-quote (erc-current-nick))
385                                "\\b")
386                        msg))))
387
388 (defun erc-match-pal-p (nickuserhost msg)
389   "Check whether NICKUSERHOST is in `erc-pals'.
390 MSG will be ignored."
391   (and nickuserhost
392        (erc-list-match erc-pals nickuserhost)))
393
394 (defun erc-match-fool-p (nickuserhost msg)
395   "Check whether NICKUSERHOST is in `erc-fools' or MSG is directed at a fool."
396   (and msg nickuserhost
397        (or (erc-list-match erc-fools nickuserhost)
398            (erc-match-directed-at-fool-p msg))))
399
400 (defun erc-match-keyword-p (nickuserhost msg)
401   "Check whether any keyword of `erc-keywords' matches for MSG.
402 NICKUSERHOST will be ignored."
403   (and msg
404        (erc-list-match
405         (mapcar (lambda (x)
406                   (if (listp x)
407                       (car x)
408                     x))
409                 erc-keywords)
410         msg)))
411
412 (defun erc-match-dangerous-host-p (nickuserhost msg)
413   "Check whether NICKUSERHOST is in `erc-dangerous-hosts'.
414 MSG will be ignored."
415   (and nickuserhost
416        (erc-list-match erc-dangerous-hosts nickuserhost)))
417
418 (defun erc-match-directed-at-fool-p (msg)
419   "Check whether MSG is directed at a fool.
420 In order to do this, every entry in `erc-fools' will be used.
421 In any of the following situations, MSG is directed at an entry FOOL:
422
423 - MSG starts with \"FOOL: \" or \"FOO, \"
424 - MSG contains \", FOOL.\" (actually, \"\\s. FOOL\\s.\")"
425   (let ((fools-beg (mapcar (lambda (entry)
426                                  (concat "^" entry "[:,] "))
427                            erc-fools))
428         (fools-end (mapcar (lambda (entry)
429                                  (concat "\\s. " entry "\\s."))
430                                erc-fools)))
431     (or (erc-list-match fools-beg msg)
432         (erc-list-match fools-end msg))))
433
434 (defun erc-get-parsed-vector (point)
435   "Return the whole parsed vector on POINT."
436   (get-text-property point 'erc-parsed))
437
438 (defun erc-get-parsed-vector-nick (vect)
439   "Return nickname in the parsed vector VECT."
440   (let* ((untreated-nick (and vect (erc-response.sender vect)))
441          (maybe-nick (when untreated-nick
442                        (car (split-string untreated-nick "!")))))
443     (when (and (not (null maybe-nick))
444                (erc-is-valid-nick-p maybe-nick))
445       untreated-nick)))
446
447 (defun erc-get-parsed-vector-type (vect)
448   "Return message type in the parsed vector VECT."
449   (and vect
450        (erc-response.command vect)))
451
452 (defun erc-match-message ()
453   "Mark certain keywords in a region.
454 Use this defun with `erc-insert-modify-hook'."
455   ;; This needs some refactoring.
456   (goto-char (point-min))
457   (let* ((to-match-nick-dep '("pal" "fool" "dangerous-host"))
458          (to-match-nick-indep '("keyword" "current-nick"))
459          (vector (erc-get-parsed-vector (point-min)))
460          (nickuserhost (erc-get-parsed-vector-nick vector))
461          (nickname (and nickuserhost
462                         (nth 0 (erc-parse-user nickuserhost))))
463          (old-pt (point))
464          (nick-beg (and nickname
465                         (re-search-forward (regexp-quote nickname)
466                                            (point-max) t)
467                         (match-beginning 0)))
468          (nick-end (when nick-beg
469                      (match-end 0)))
470          (message (buffer-substring (if (and nick-end
471                                              (<= (+ 2 nick-end) (point-max)))
472                                         (+ 2 nick-end)
473                                       (point-min))
474                                     (point-max))))
475     (when vector
476       (mapc
477        (lambda (match-type)
478          (goto-char (point-min))
479          (let* ((match-prefix (concat "erc-" match-type))
480                 (match-pred (intern (concat "erc-match-" match-type "-p")))
481                 (match-htype (eval (intern (concat match-prefix
482                                                    "-highlight-type"))))
483                 (match-regex (if (string= match-type "current-nick")
484                                  (regexp-quote (erc-current-nick))
485                                (eval (intern (concat match-prefix "s")))))
486                 (match-face (intern (concat match-prefix "-face"))))
487            (when (funcall match-pred nickuserhost message)
488              (cond
489               ;; Highlight the nick of the message
490               ((and (eq match-htype 'nick)
491                     nick-end)
492                (erc-put-text-property
493                 nick-beg nick-end
494                 'face match-face (current-buffer)))
495               ;; Highlight the nick of the message, or the current
496               ;; nick if there's no nick in the message (e.g. /NAMES
497               ;; output)
498               ((and (string= match-type "current-nick")
499                     (eq match-htype 'nick-or-keyword))
500                (if nick-end
501                    (erc-put-text-property
502                     nick-beg nick-end
503                     'face match-face (current-buffer))
504                  (goto-char (+ 2 (or nick-end
505                                      (point-min))))
506                  (while (re-search-forward match-regex nil t)
507                    (erc-put-text-property (match-beginning 0) (match-end 0)
508                                           'face match-face))))
509               ;; Highlight the whole message
510               ((eq match-htype 'all)
511                (erc-put-text-property
512                 (point-min) (point-max)
513                 'face match-face (current-buffer)))
514               ;; Highlight all occurrences of the word to be
515               ;; highlighted.
516               ((and (string= match-type "keyword")
517                     (eq match-htype 'keyword))
518                (mapc (lambda (elt)
519                        (let ((regex elt)
520                              (face match-face))
521                          (when (consp regex)
522                            (setq regex (car elt)
523                                  face (cdr elt)))
524                          (goto-char (+ 2 (or nick-end
525                                              (point-min))))
526                          (while (re-search-forward regex nil t)
527                            (erc-put-text-property
528                             (match-beginning 0) (match-end 0)
529                             'face face))))
530                      match-regex))
531               ;; Highlight all occurrences of our nick.
532               ((and (string= match-type "current-nick")
533                     (eq match-htype 'keyword))
534                (goto-char (+ 2 (or nick-end
535                                    (point-min))))
536                (while (re-search-forward match-regex nil t)
537                  (erc-put-text-property (match-beginning 0) (match-end 0)
538                                         'face match-face)))
539               ;; Else twiddle your thumbs.
540               (t nil))
541              (run-hook-with-args
542               'erc-text-matched-hook
543               (intern match-type)
544               (or nickuserhost
545                   (concat "Server:" (erc-get-parsed-vector-type vector)))
546               message))))
547        (if nickuserhost
548            (append to-match-nick-dep to-match-nick-indep)
549          to-match-nick-indep)))))
550
551 (defun erc-log-matches (match-type nickuserhost message)
552   "Log matches in a separate buffer, determined by MATCH-TYPE.
553 The behaviour of this function is controlled by the variables
554 `erc-log-matches-types-alist' and `erc-log-matches-flag'. Specify the
555 match types which should be logged in the former, and
556 deactivate/activate match logging in the latter. See
557 `erc-log-match-format'."
558   (let  ((match-buffer-name (cdr (assq match-type
559                                        erc-log-matches-types-alist)))
560          (nick (nth 0 (erc-parse-user nickuserhost))))
561     (when (and
562            (or (eq erc-log-matches-flag t)
563                (and (eq erc-log-matches-flag 'away)
564                     erc-away))
565            match-buffer-name)
566       (let ((line (format-spec erc-log-match-format
567                    (format-spec-make
568                     ?n nick
569                     ?t (format-time-string
570                         (or (and (boundp 'erc-timestamp-format)
571                                  erc-timestamp-format)
572                             "[%Y-%m-%d %H:%M] "))
573                     ?c (or (erc-default-target) "")
574                     ?m message
575                     ?u nickuserhost))))
576         (with-current-buffer (erc-log-matches-make-buffer match-buffer-name)
577           (toggle-read-only -1)
578           (point-max)
579           (insert line)
580           (toggle-read-only 1))))))
581
582 (defun erc-log-matches-make-buffer (name)
583   "Create or get a log-matches buffer named NAME and return it."
584   (let* ((buffer-already (get-buffer name))
585          (buffer (or buffer-already
586                      (get-buffer-create name))))
587     (with-current-buffer buffer
588       (unless buffer-already
589         (insert " == Type \"q\" to dismiss messages ==\n")
590         (erc-view-mode-enter nil (lambda (buffer)
591                                    (when (y-or-n-p "Discard messages?")
592                                      (kill-buffer buffer)))))
593       buffer)))
594
595 (defun erc-log-matches-come-back (proc parsed)
596   "Display a notice that messages were logged while away."
597   (when (and erc-away
598              (eq erc-log-matches-flag 'away))
599     (mapc
600      (lambda (match-type)
601        (let ((buffer (get-buffer (cdr match-type)))
602              (buffer-name (cdr match-type)))
603          (when buffer
604            (let* ((last-msg-time (erc-emacs-time-to-erc-time
605                                   (with-current-buffer buffer
606                                     (get-text-property (1- (point-max))
607                                                        'timestamp))))
608                   (away-time     (erc-emacs-time-to-erc-time erc-away)))
609              (when (and away-time last-msg-time
610                         (erc-time-gt last-msg-time away-time))
611                (erc-display-message
612                 nil 'notice 'active
613                 (format "You have logged messages waiting in \"%s\"."
614                         buffer-name))
615                (erc-display-message
616                 nil 'notice 'active
617                 (format "Type \"C-c C-k %s RET\" to view them."
618                         buffer-name)))))))
619      erc-log-matches-types-alist))
620   nil)
621
622 ; This handler must be run _before_ erc-process-away is.
623 (add-hook 'erc-server-305-functions 'erc-log-matches-come-back nil)
624
625 (defun erc-go-to-log-matches-buffer ()
626   "Interactively open an erc-log-matches buffer."
627   (interactive)
628   (let ((buffer-name (completing-read "Switch to ERC Log buffer: "
629                                       (mapcar (lambda (x)
630                                                 (cons (cdr x) t))
631                                               erc-log-matches-types-alist)
632                                       (lambda (buffer-cons)
633                                         (get-buffer (car buffer-cons))))))
634     (switch-to-buffer buffer-name)))
635
636 (define-key erc-mode-map "\C-c\C-k" 'erc-go-to-log-matches-buffer)
637
638 (defun erc-hide-fools (match-type nickuserhost message)
639  "Hide foolish comments.
640 This function should be called from `erc-text-matched-hook'."
641  (when (eq match-type 'fool)
642    (erc-put-text-properties (point-min) (point-max)
643                             '(invisible intangible)
644                             (current-buffer))))
645
646 (defun erc-beep-on-match (match-type nickuserhost message)
647   "Beep when text matches.
648 This function is meant to be called from `erc-text-matched-hook'."
649   (when (member match-type erc-beep-match-types)
650     (beep)))
651
652 (provide 'erc-match)
653
654 ;;; erc-match.el ends here
655 ;;
656 ;; Local Variables:
657 ;; indent-tabs-mode: t
658 ;; tab-width: 8
659 ;; End:
660
661 ;; arch-tag: 1f1f595e-abcc-4b0b-83db-598a1d3f0f82