Merge changes made in Gnus trunk.
[bpt/emacs.git] / lisp / gnus / gnus-registry.el
CommitLineData
23f87bed 1;;; gnus-registry.el --- article registry for Gnus
e84b4b86 2
c9fc72fa 3;;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
0ab5c2be 4;;; Free Software Foundation, Inc.
23f87bed
MB
5
6;; Author: Ted Zlatanov <tzz@lifelogs.com>
0ab5c2be 7;; Keywords: news registry
23f87bed
MB
8
9;; This file is part of GNU Emacs.
10
5e809f55 11;; GNU Emacs is free software: you can redistribute it and/or modify
23f87bed 12;; it under the terms of the GNU General Public License as published by
5e809f55
GM
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
23f87bed
MB
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
5e809f55 18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23f87bed
MB
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
5e809f55 22;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
23f87bed
MB
23
24;;; Commentary:
25
01c52d31
MB
26;; This is the gnus-registry.el package, which works with all
27;; backends, not just nnmail (e.g. NNTP). The major issue is that it
28;; doesn't go across backends, so for instance if an article is in
29;; nnml:sys and you see a reference to it in nnimap splitting, the
30;; article will end up in nnimap:sys
23f87bed
MB
31
32;; gnus-registry.el intercepts article respooling, moving, deleting,
33;; and copying for all backends. If it doesn't work correctly for
34;; you, submit a bug report and I'll be glad to fix it. It needs
35;; documentation in the manual (also on my to-do list).
36
37;; Put this in your startup file (~/.gnus.el for instance)
38
a3f57c41 39;; (require 'nnregistry) ;; optional, or see below (automatically calls `gnus-registry-install-nnregistry' when `gnus-registry-initialize' is called)
23f87bed
MB
40;; (setq gnus-registry-max-entries 2500
41;; gnus-registry-use-long-group-names t)
42
43;; (gnus-registry-initialize)
a3f57c41 44;; (gnus-registry-install-nnregistry) ;; optional, or see above (loading nnregistry makes it unnecessary)
23f87bed
MB
45
46;; Then use this in your fancy-split:
47
48;; (: gnus-registry-split-fancy-with-parent)
49
50;; TODO:
51
52;; - get the correct group on spool actions
53
54;; - articles that are spooled to a different backend should be handled
55
56;;; Code:
57
58(eval-when-compile (require 'cl))
59
60(require 'gnus)
61(require 'gnus-int)
62(require 'gnus-sum)
996aa8c1 63(require 'gnus-util)
23f87bed 64(require 'nnmail)
ec7995fa 65(require 'easymenu)
23f87bed 66
9efa445f
DN
67(defvar gnus-adaptive-word-syntax-table)
68
23f87bed
MB
69(defvar gnus-registry-dirty t
70 "Boolean set to t when the registry is modified")
71
72(defgroup gnus-registry nil
73 "The Gnus registry."
bf247b6e 74 :version "22.1"
23f87bed
MB
75 :group 'gnus)
76
c9fc72fa 77(defvar gnus-registry-hashtb (make-hash-table
01c52d31
MB
78 :size 256
79 :test 'equal)
23f87bed
MB
80 "*The article registry by Message ID.")
81
0b6799c3 82(defcustom gnus-registry-marks
14e8de0c 83 '((Important
8f7abae3
MB
84 :char ?i
85 :image "summary_important")
14e8de0c 86 (Work
8f7abae3
MB
87 :char ?w
88 :image "summary_work")
14e8de0c 89 (Personal
8f7abae3
MB
90 :char ?p
91 :image "summary_personal")
14e8de0c 92 (To-Do
8f7abae3
MB
93 :char ?t
94 :image "summary_todo")
14e8de0c 95 (Later
8f7abae3
MB
96 :char ?l
97 :image "summary_later"))
14e8de0c
MB
98
99 "List of registry marks and their options.
100
101`gnus-registry-mark-article' will offer symbols from this list
c9fc72fa 102for completion.
14e8de0c
MB
103
104Each entry must have a character to be useful for summary mode
105line display and for keyboard shortcuts.
106
107Each entry must have an image string to be useful for visual
108display."
0b6799c3 109 :group 'gnus-registry
8f7abae3
MB
110 :type '(repeat :tag "Registry Marks"
111 (cons :tag "Mark"
112 (symbol :tag "Name")
113 (checklist :tag "Options" :greedy t
114 (group :inline t
115 (const :format "" :value :char)
116 (character :tag "Character code"))
117 (group :inline t
118 (const :format "" :value :image)
119 (string :tag "Image"))))))
0b6799c3
MB
120
121(defcustom gnus-registry-default-mark 'To-Do
14e8de0c 122 "The default mark. Should be a valid key for `gnus-registry-marks'."
0b6799c3
MB
123 :group 'gnus-registry
124 :type 'symbol)
125
c9fc72fa 126(defcustom gnus-registry-unfollowed-groups
6b958814 127 '("delayed$" "drafts$" "queue$" "INBOX$" "^nnmairix:")
01c52d31
MB
128 "List of groups that gnus-registry-split-fancy-with-parent won't return.
129The group names are matched, they don't have to be fully
130qualified. This parameter tells the Registry 'never split a
131message into a group that matches one of these, regardless of
6b958814
G
132references.'
133
134nnmairix groups are specifically excluded because they are ephemeral."
23f87bed 135 :group 'gnus-registry
01c52d31 136 :type '(repeat regexp))
23f87bed 137
8f7abae3 138(defcustom gnus-registry-install 'ask
23f87bed
MB
139 "Whether the registry should be installed."
140 :group 'gnus-registry
8f7abae3
MB
141 :type '(choice (const :tag "Never Install" nil)
142 (const :tag "Always Install" t)
143 (const :tag "Ask Me" ask)))
23f87bed 144
ec7995fa
KY
145(defvar gnus-summary-misc-menu) ;; Avoid byte compiler warning.
146
147(defvar gnus-registry-misc-menus nil) ; ugly way to keep the menus
148
23f87bed
MB
149(defcustom gnus-registry-clean-empty t
150 "Whether the empty registry entries should be deleted.
01c52d31
MB
151Registry entries are considered empty when they have no groups
152and no extra data."
23f87bed
MB
153 :group 'gnus-registry
154 :type 'boolean)
155
64763fe3
MB
156(defcustom gnus-registry-use-long-group-names t
157 "Whether the registry should use long group names."
23f87bed
MB
158 :group 'gnus-registry
159 :type 'boolean)
160
b86402ab
MB
161(defcustom gnus-registry-max-track-groups 20
162 "The maximum number of non-unique group matches to check for a message ID."
163 :group 'gnus-registry
164 :type '(radio (const :format "Unlimited " nil)
165 (integer :format "Maximum non-unique matches: %v")))
166
23f87bed
MB
167(defcustom gnus-registry-track-extra nil
168 "Whether the registry should track extra data about a message.
169The Subject and Sender (From:) headers are currently tracked this
170way."
171 :group 'gnus-registry
bf247b6e 172 :type
23f87bed
MB
173 '(set :tag "Tracking choices"
174 (const :tag "Track by subject (Subject: header)" subject)
175 (const :tag "Track by sender (From: header)" sender)))
176
58a67d68
MB
177(defcustom gnus-registry-split-strategy nil
178 "Whether the registry should track extra data about a message.
179The Subject and Sender (From:) headers are currently tracked this
180way."
181 :group 'gnus-registry
182 :type
183 '(choice :tag "Tracking choices"
184 (const :tag "Only use single choices, discard multiple matches" nil)
185 (const :tag "Majority of matches wins" majority)
186 (const :tag "First found wins" first)))
187
23f87bed
MB
188(defcustom gnus-registry-entry-caching t
189 "Whether the registry should cache extra information."
190 :group 'gnus-registry
191 :type 'boolean)
192
193(defcustom gnus-registry-minimum-subject-length 5
194 "The minimum length of a subject before it's considered trackable."
195 :group 'gnus-registry
196 :type 'integer)
197
198(defcustom gnus-registry-trim-articles-without-groups t
199 "Whether the registry should clean out message IDs without groups."
200 :group 'gnus-registry
201 :type 'boolean)
202
0b6799c3
MB
203(defcustom gnus-registry-extra-entries-precious '(marks)
204 "What extra entries are precious, meaning they won't get trimmed.
205When you save the Gnus registry, it's trimmed to be no longer
206than `gnus-registry-max-entries' (which is nil by default, so no
207trimming happens). Any entries with extra data in this list (by
208default, marks are included, so articles with marks are
209considered precious) will not be trimmed."
210 :group 'gnus-registry
211 :type '(repeat symbol))
212
c9fc72fa
LMI
213(defcustom gnus-registry-cache-file
214 (nnheader-concat
215 (or gnus-dribble-directory gnus-home-directory "~/")
01c52d31 216 ".gnus.registry.eld")
23f87bed
MB
217 "File where the Gnus registry will be stored."
218 :group 'gnus-registry
219 :type 'file)
220
221(defcustom gnus-registry-max-entries nil
222 "Maximum number of entries in the registry, nil for unlimited."
223 :group 'gnus-registry
224 :type '(radio (const :format "Unlimited " nil)
ad136a7c 225 (integer :format "Maximum number: %v")))
23f87bed 226
23f87bed
MB
227(defun gnus-registry-track-subject-p ()
228 (memq 'subject gnus-registry-track-extra))
229
230(defun gnus-registry-track-sender-p ()
231 (memq 'sender gnus-registry-track-extra))
232
233(defun gnus-registry-cache-read ()
234 "Read the registry cache file."
235 (interactive)
236 (let ((file gnus-registry-cache-file))
237 (when (file-exists-p file)
238 (gnus-message 5 "Reading %s..." file)
239 (gnus-load file)
240 (gnus-message 5 "Reading %s...done" file))))
241
8aed9ac5
RS
242;; FIXME: Get rid of duplicated code, cf. `gnus-save-newsrc-file' in
243;; `gnus-start.el'. --rsteib
23f87bed
MB
244(defun gnus-registry-cache-save ()
245 "Save the registry cache file."
246 (interactive)
247 (let ((file gnus-registry-cache-file))
20a673b2 248 (with-current-buffer (gnus-get-buffer-create " *Gnus-registry-cache*")
23f87bed
MB
249 (make-local-variable 'version-control)
250 (setq version-control gnus-backup-startup-file)
251 (setq buffer-file-name file)
252 (setq default-directory (file-name-directory buffer-file-name))
253 (buffer-disable-undo)
254 (erase-buffer)
255 (gnus-message 5 "Saving %s..." file)
256 (if gnus-save-startup-file-via-temp-buffer
257 (let ((coding-system-for-write gnus-ding-file-coding-system)
258 (standard-output (current-buffer)))
c9fc72fa 259 (gnus-gnus-to-quick-newsrc-format
14e8de0c 260 t "gnus registry startup file" 'gnus-registry-alist)
23f87bed
MB
261 (gnus-registry-cache-whitespace file)
262 (save-buffer))
263 (let ((coding-system-for-write gnus-ding-file-coding-system)
264 (version-control gnus-backup-startup-file)
265 (startup-file file)
266 (working-dir (file-name-directory file))
267 working-file
268 (i -1))
269 ;; Generate the name of a non-existent file.
270 (while (progn (setq working-file
271 (format
272 (if (and (eq system-type 'ms-dos)
273 (not (gnus-long-file-names)))
274 "%s#%d.tm#" ; MSDOS limits files to 8+3
7c2fb837 275 "%s#tmp#%d")
23f87bed
MB
276 working-dir (setq i (1+ i))))
277 (file-exists-p working-file)))
bf247b6e 278
23f87bed
MB
279 (unwind-protect
280 (progn
281 (gnus-with-output-to-file working-file
c9fc72fa 282 (gnus-gnus-to-quick-newsrc-format
14e8de0c 283 t "gnus registry startup file" 'gnus-registry-alist))
bf247b6e 284
23f87bed
MB
285 ;; These bindings will mislead the current buffer
286 ;; into thinking that it is visiting the startup
287 ;; file.
288 (let ((buffer-backed-up nil)
289 (buffer-file-name startup-file)
290 (file-precious-flag t)
291 (setmodes (file-modes startup-file)))
292 ;; Backup the current version of the startup file.
293 (backup-buffer)
bf247b6e 294
23f87bed
MB
295 ;; Replace the existing startup file with the temp file.
296 (rename-file working-file startup-file t)
01c52d31 297 (gnus-set-file-modes startup-file setmodes)))
23f87bed
MB
298 (condition-case nil
299 (delete-file working-file)
300 (file-error nil)))))
bf247b6e 301
23f87bed
MB
302 (gnus-kill-buffer (current-buffer))
303 (gnus-message 5 "Saving %s...done" file))))
304
305;; Idea from Dan Christensen <jdc@chow.mat.jhu.edu>
306;; Save the gnus-registry file with extra line breaks.
307(defun gnus-registry-cache-whitespace (filename)
01c52d31 308 (gnus-message 7 "Adding whitespace to %s" filename)
23f87bed
MB
309 (save-excursion
310 (goto-char (point-min))
311 (while (re-search-forward "^(\\|(\\\"" nil t)
312 (replace-match "\n\\&" t))
313 (goto-char (point-min))
314 (while (re-search-forward " $" nil t)
315 (replace-match "" t t))))
316
317(defun gnus-registry-save (&optional force)
318 (when (or gnus-registry-dirty force)
319 (let ((caching gnus-registry-entry-caching))
320 ;; turn off entry caching, so mtime doesn't get recorded
321 (setq gnus-registry-entry-caching nil)
322 ;; remove entry caches
323 (maphash
324 (lambda (key value)
325 (if (hash-table-p value)
326 (remhash key gnus-registry-hashtb)))
327 gnus-registry-hashtb)
328 ;; remove empty entries
bf247b6e 329 (when gnus-registry-clean-empty
23f87bed 330 (gnus-registry-clean-empty-function))
01c52d31 331 ;; now trim and clean text properties from the registry appropriately
c9fc72fa 332 (setq gnus-registry-alist
01c52d31
MB
333 (gnus-registry-remove-alist-text-properties
334 (gnus-registry-trim
335 (gnus-hashtable-to-alist
336 gnus-registry-hashtb))))
23f87bed
MB
337 ;; really save
338 (gnus-registry-cache-save)
339 (setq gnus-registry-entry-caching caching)
340 (setq gnus-registry-dirty nil))))
341
342(defun gnus-registry-clean-empty-function ()
343 "Remove all empty entries from the registry. Returns count thereof."
344 (let ((count 0))
01c52d31 345
23f87bed
MB
346 (maphash
347 (lambda (key value)
01c52d31
MB
348 (when (stringp key)
349 (dolist (group (gnus-registry-fetch-groups key))
350 (when (gnus-parameter-registry-ignore group)
351 (gnus-message
c9fc72fa 352 10
01c52d31
MB
353 "gnus-registry: deleted ignored group %s from key %s"
354 group key)
355 (gnus-registry-delete-group key group)))
356
357 (unless (gnus-registry-group-count key)
358 (gnus-registry-delete-id key))
359
360 (unless (or
361 (gnus-registry-fetch-group key)
362 ;; TODO: look for specific extra data here!
363 ;; in this example, we look for 'label
364 (gnus-registry-fetch-extra key 'label))
365 (incf count)
366 (gnus-registry-delete-id key))
c9fc72fa 367
01c52d31 368 (unless (stringp key)
c9fc72fa
LMI
369 (gnus-message
370 10
371 "gnus-registry key %s was not a string, removing"
01c52d31
MB
372 key)
373 (gnus-registry-delete-id key))))
c9fc72fa 374
23f87bed
MB
375 gnus-registry-hashtb)
376 count))
377
378(defun gnus-registry-read ()
379 (gnus-registry-cache-read)
996aa8c1 380 (setq gnus-registry-hashtb (gnus-alist-to-hashtable gnus-registry-alist))
23f87bed
MB
381 (setq gnus-registry-dirty nil))
382
01c52d31
MB
383(defun gnus-registry-remove-alist-text-properties (v)
384 "Remove text properties from all strings in alist."
385 (if (stringp v)
386 (gnus-string-remove-all-properties v)
387 (if (and (listp v) (listp (cdr v)))
388 (mapcar 'gnus-registry-remove-alist-text-properties v)
389 (if (and (listp v) (stringp (cdr v)))
390 (cons (gnus-registry-remove-alist-text-properties (car v))
391 (gnus-registry-remove-alist-text-properties (cdr v)))
392 v))))
393
23f87bed 394(defun gnus-registry-trim (alist)
01c52d31 395 "Trim alist to size, using gnus-registry-max-entries.
0b6799c3 396Any entries with extra data (marks, currently) are left alone."
c9fc72fa 397 (if (null gnus-registry-max-entries)
7cb0aa56 398 alist ; just return the alist
23f87bed 399 ;; else, when given max-entries, trim the alist
7cb0aa56 400 (let* ((timehash (make-hash-table
0b6799c3
MB
401 :size 20000
402 :test 'equal))
403 (precious (make-hash-table
404 :size 20000
7cb0aa56
MB
405 :test 'equal))
406 (trim-length (- (length alist) gnus-registry-max-entries))
0b6799c3
MB
407 (trim-length (if (natnump trim-length) trim-length 0))
408 precious-list junk-list)
23f87bed
MB
409 (maphash
410 (lambda (key value)
0b6799c3
MB
411 (let ((extra (gnus-registry-fetch-extra key)))
412 (dolist (item gnus-registry-extra-entries-precious)
413 (dolist (e extra)
414 (when (equal (nth 0 e) item)
415 (puthash key t precious)
416 (return))))
417 (puthash key (gnus-registry-fetch-extra key 'mtime) timehash)))
23f87bed 418 gnus-registry-hashtb)
23f87bed 419
0b6799c3 420 (dolist (item alist)
3d0f8a67 421 (let ((key (nth 0 item)))
0b6799c3
MB
422 (if (gethash key precious)
423 (push item precious-list)
424 (push item junk-list))))
425
c9fc72fa 426 (sort
0b6799c3
MB
427 junk-list
428 (lambda (a b)
c9fc72fa 429 (let ((t1 (or (cdr (gethash (car a) timehash))
0b6799c3 430 '(0 0 0)))
c9fc72fa 431 (t2 (or (cdr (gethash (car b) timehash))
0b6799c3
MB
432 '(0 0 0))))
433 (time-less-p t1 t2))))
434
435 ;; we use the return value of this setq, which is the trimmed alist
436 (setq alist (append precious-list
437 (nthcdr trim-length junk-list))))))
c9fc72fa 438
23f87bed
MB
439(defun gnus-registry-action (action data-header from &optional to method)
440 (let* ((id (mail-header-id data-header))
01c52d31
MB
441 (subject (gnus-string-remove-all-properties
442 (gnus-registry-simplify-subject
443 (mail-header-subject data-header))))
c9fc72fa 444 (sender (gnus-string-remove-all-properties
14e8de0c 445 (mail-header-from data-header)))
23f87bed
MB
446 (from (gnus-group-guess-full-name-from-command-method from))
447 (to (if to (gnus-group-guess-full-name-from-command-method to) nil))
448 (to-name (if to to "the Bit Bucket"))
449 (old-entry (gethash id gnus-registry-hashtb)))
01c52d31 450 (gnus-message 7 "Registry: article %s %s from %s to %s"
23f87bed
MB
451 id
452 (if method "respooling" "going")
453 from
454 to)
455
456 ;; All except copy will need a delete
457 (gnus-registry-delete-group id from)
458
bf247b6e 459 (when (equal 'copy action)
23f87bed
MB
460 (gnus-registry-add-group id from subject sender)) ; undo the delete
461
462 (gnus-registry-add-group id to subject sender)))
463
464(defun gnus-registry-spool-action (id group &optional subject sender)
465 (let ((group (gnus-group-guess-full-name-from-command-method group)))
466 (when (and (stringp id) (string-match "\r$" id))
467 (setq id (substring id 0 -1)))
01c52d31 468 (gnus-message 7 "Registry: article %s spooled to %s"
23f87bed
MB
469 id
470 group)
471 (gnus-registry-add-group id group subject sender)))
472
473;; Function for nn{mail|imap}-split-fancy: look up all references in
474;; the cache and if a match is found, return that group.
475(defun gnus-registry-split-fancy-with-parent ()
476 "Split this message into the same group as its parent. The parent
477is obtained from the registry. This function can be used as an entry
478in `nnmail-split-fancy' or `nnimap-split-fancy', for example like
bf247b6e 479this: (: gnus-registry-split-fancy-with-parent)
23f87bed 480
01c52d31
MB
481This function tracks ALL backends, unlike
482`nnmail-split-fancy-with-parent' which tracks only nnmail
483messages.
484
23f87bed 485For a message to be split, it looks for the parent message in the
01c52d31
MB
486References or In-Reply-To header and then looks in the registry
487to see which group that message was put in. This group is
14e8de0c
MB
488returned, unless `gnus-registry-follow-group-p' return nil for
489that group.
23f87bed
MB
490
491See the Info node `(gnus)Fancy Mail Splitting' for more details."
14e8de0c
MB
492 (let* ((refstr (or (message-fetch-field "references") "")) ; guaranteed
493 (reply-to (message-fetch-field "in-reply-to")) ; may be nil
01c52d31 494 ;; now, if reply-to is valid, append it to the References
c9fc72fa 495 (refstr (if reply-to
01c52d31
MB
496 (concat refstr " " reply-to)
497 refstr))
14e8de0c
MB
498 ;; these may not be used, but the code is cleaner having them up here
499 (sender (gnus-string-remove-all-properties
500 (message-fetch-field "from")))
501 (subject (gnus-string-remove-all-properties
502 (gnus-registry-simplify-subject
503 (message-fetch-field "subject"))))
504
505 (nnmail-split-fancy-with-parent-ignore-groups
506 (if (listp nnmail-split-fancy-with-parent-ignore-groups)
507 nnmail-split-fancy-with-parent-ignore-groups
508 (list nnmail-split-fancy-with-parent-ignore-groups)))
509 (log-agent "gnus-registry-split-fancy-with-parent")
58a67d68 510 found found-full)
14e8de0c
MB
511
512 ;; this is a big if-else statement. it uses
513 ;; gnus-registry-post-process-groups to filter the results after
514 ;; every step.
515 (cond
516 ;; the references string must be valid and parse to valid references
517 ((and refstr (gnus-extract-references refstr))
518 (dolist (reference (nreverse (gnus-extract-references refstr)))
519 (gnus-message
520 9
521 "%s is looking for matches for reference %s from [%s]"
522 log-agent reference refstr)
c9fc72fa
LMI
523 (dolist (group (gnus-registry-fetch-groups
524 reference
b86402ab 525 gnus-registry-max-track-groups))
14e8de0c 526 (when (and group (gnus-registry-follow-group-p group))
23f87bed 527 (gnus-message
14e8de0c
MB
528 7
529 "%s traced the reference %s from [%s] to group %s"
530 log-agent reference refstr group)
531 (push group found))))
532 ;; filter the found groups and return them
58a67d68 533 ;; the found groups are the full groups
c9fc72fa 534 (setq found (gnus-registry-post-process-groups
58a67d68 535 "references" refstr found found)))
c9fc72fa 536
14e8de0c 537 ;; else: there were no matches, now try the extra tracking by sender
8336c962
MB
538 ((and (gnus-registry-track-sender-p)
539 sender
0ab5c2be
MB
540 (not (equal (gnus-extract-address-component-email sender)
541 user-mail-address)))
14e8de0c
MB
542 (maphash
543 (lambda (key value)
544 (let ((this-sender (cdr
545 (gnus-registry-fetch-extra key 'sender)))
546 matches)
547 (when (and this-sender
548 (equal sender this-sender))
c9fc72fa 549 (let ((groups (gnus-registry-fetch-groups
b86402ab
MB
550 key
551 gnus-registry-max-track-groups)))
9b3ebcb6 552 (dolist (group groups)
58a67d68 553 (push group found-full)
9b3ebcb6 554 (setq found (append (list group) (delete group found)))))
14e8de0c
MB
555 (push key matches)
556 (gnus-message
557 ;; raise level of messaging if gnus-registry-track-extra
558 (if gnus-registry-track-extra 7 9)
559 "%s (extra tracking) traced sender %s to groups %s (keys %s)"
560 log-agent sender found matches))))
561 gnus-registry-hashtb)
562 ;; filter the found groups and return them
58a67d68 563 ;; the found groups are NOT the full groups
c9fc72fa 564 (setq found (gnus-registry-post-process-groups
58a67d68 565 "sender" sender found found-full)))
c9fc72fa 566
14e8de0c
MB
567 ;; else: there were no matches, now try the extra tracking by subject
568 ((and (gnus-registry-track-subject-p)
569 subject
570 (< gnus-registry-minimum-subject-length (length subject)))
571 (maphash
572 (lambda (key value)
573 (let ((this-subject (cdr
574 (gnus-registry-fetch-extra key 'subject)))
575 matches)
576 (when (and this-subject
577 (equal subject this-subject))
c9fc72fa 578 (let ((groups (gnus-registry-fetch-groups
b86402ab
MB
579 key
580 gnus-registry-max-track-groups)))
9b3ebcb6 581 (dolist (group groups)
58a67d68 582 (push group found-full)
9b3ebcb6 583 (setq found (append (list group) (delete group found)))))
14e8de0c
MB
584 (push key matches)
585 (gnus-message
586 ;; raise level of messaging if gnus-registry-track-extra
587 (if gnus-registry-track-extra 7 9)
588 "%s (extra tracking) traced subject %s to groups %s (keys %s)"
589 log-agent subject found matches))))
590 gnus-registry-hashtb)
591 ;; filter the found groups and return them
58a67d68 592 ;; the found groups are NOT the full groups
c9fc72fa 593 (setq found (gnus-registry-post-process-groups
58a67d68
MB
594 "subject" subject found found-full))))
595 ;; after the (cond) we extract the actual value safely
596 (car-safe found)))
14e8de0c 597
58a67d68 598(defun gnus-registry-post-process-groups (mode key groups groups-full)
14e8de0c
MB
599 "Modifies GROUPS found by MODE for KEY to determine which ones to follow.
600
601MODE can be 'subject' or 'sender' for example. The KEY is the
602value by which MODE was searched.
603
604Transforms each group name to the equivalent short name.
605
606Checks if the current Gnus method (from `gnus-command-method' or
607from `gnus-newsgroup-name') is the same as the group's method.
608This is not possible if gnus-registry-use-long-group-names is
609false. Foreign methods are not supported so they are rejected.
610
611Reduces the list to a single group, or complains if that's not
58a67d68
MB
612possible. Uses `gnus-registry-split-strategy' and GROUPS-FULL if
613necessary."
14e8de0c
MB
614 (let ((log-agent "gnus-registry-post-process-group")
615 out)
58a67d68
MB
616
617 ;; the strategy can be 'first, 'majority, or nil
618 (when (eq gnus-registry-split-strategy 'first)
619 (when groups
620 (setq groups (list (car-safe groups)))))
621
622 (when (eq gnus-registry-split-strategy 'majority)
623 (let ((freq (make-hash-table
624 :size 256
625 :test 'equal)))
626 (mapc (lambda(x) (puthash x (1+ (gethash x freq 0)) freq)) groups-full)
627 (setq groups (list (car-safe
628 (sort
629 groups
630 (lambda (a b)
631 (> (gethash a freq 0)
632 (gethash b freq 0)))))))))
c9fc72fa 633
14e8de0c
MB
634 (if gnus-registry-use-long-group-names
635 (dolist (group groups)
636 (let ((m1 (gnus-find-method-for-group group))
637 (m2 (or gnus-command-method
638 (gnus-find-method-for-group gnus-newsgroup-name)))
639 (short-name (gnus-group-short-name group)))
640 (if (gnus-methods-equal-p m1 m2)
641 (progn
642 ;; this is REALLY just for debugging
643 (gnus-message
644 10
645 "%s stripped group %s to %s"
646 log-agent group short-name)
647 (unless (member short-name out)
648 (push short-name out)))
649 ;; else...
650 (gnus-message
651 7
652 "%s ignored foreign group %s"
653 log-agent group))))
654 (setq out groups))
655 (when (cdr-safe out)
23f87bed 656 (gnus-message
14e8de0c
MB
657 5
658 "%s: too many extra matches (%s) for %s %s. Returning none."
659 log-agent out mode key)
660 (setq out nil))
661 out))
662
663(defun gnus-registry-follow-group-p (group)
664 "Determines if a group name should be followed.
665Consults `gnus-registry-unfollowed-groups' and
666`nnmail-split-fancy-with-parent-ignore-groups'."
77154961 667 (not (or (gnus-grep-in-list
14e8de0c
MB
668 group
669 gnus-registry-unfollowed-groups)
77154961 670 (gnus-grep-in-list
14e8de0c
MB
671 group
672 nnmail-split-fancy-with-parent-ignore-groups))))
23f87bed 673
01c52d31
MB
674(defun gnus-registry-wash-for-keywords (&optional force)
675 (interactive)
676 (let ((id (gnus-registry-fetch-message-id-fast gnus-current-article))
677 word words)
678 (if (or (not (gnus-registry-fetch-extra id 'keywords))
679 force)
20a673b2 680 (with-current-buffer gnus-article-buffer
01c52d31
MB
681 (article-goto-body)
682 (save-window-excursion
683 (save-restriction
684 (narrow-to-region (point) (point-max))
685 (with-syntax-table gnus-adaptive-word-syntax-table
686 (while (re-search-forward "\\b\\w+\\b" nil t)
687 (setq word (gnus-registry-remove-alist-text-properties
688 (downcase (buffer-substring
689 (match-beginning 0) (match-end 0)))))
690 (if (> (length word) 3)
691 (push word words))))))
692 (gnus-registry-store-extra-entry id 'keywords words)))))
693
694(defun gnus-registry-find-keywords (keyword)
695 (interactive "skeyword: ")
696 (let (articles)
697 (maphash
698 (lambda (key value)
0ab5c2be
MB
699 (when (member keyword
700 (cdr-safe (gnus-registry-fetch-extra key 'keywords)))
01c52d31
MB
701 (push key articles)))
702 gnus-registry-hashtb)
703 articles))
704
23f87bed
MB
705(defun gnus-registry-register-message-ids ()
706 "Register the Message-ID of every article in the group"
707 (unless (gnus-parameter-registry-ignore gnus-newsgroup-name)
708 (dolist (article gnus-newsgroup-articles)
709 (let ((id (gnus-registry-fetch-message-id-fast article)))
3d0f8a67 710 (unless (member gnus-newsgroup-name (gnus-registry-fetch-groups id))
bf247b6e 711 (gnus-message 9 "Registry: Registering article %d with group %s"
23f87bed 712 article gnus-newsgroup-name)
c9fc72fa
LMI
713 (gnus-registry-add-group
714 id
23f87bed
MB
715 gnus-newsgroup-name
716 (gnus-registry-fetch-simplified-message-subject-fast article)
717 (gnus-registry-fetch-sender-fast article)))))))
718
719(defun gnus-registry-fetch-message-id-fast (article)
720 "Fetch the Message-ID quickly, using the internal gnus-data-list function"
721 (if (and (numberp article)
722 (assoc article (gnus-data-list nil)))
723 (mail-header-id (gnus-data-header (assoc article (gnus-data-list nil))))
724 nil))
725
726(defun gnus-registry-simplify-subject (subject)
727 (if (stringp subject)
728 (gnus-simplify-subject subject)
729 nil))
730
731(defun gnus-registry-fetch-simplified-message-subject-fast (article)
732 "Fetch the Subject quickly, using the internal gnus-data-list function"
733 (if (and (numberp article)
734 (assoc article (gnus-data-list nil)))
01c52d31
MB
735 (gnus-string-remove-all-properties
736 (gnus-registry-simplify-subject
737 (mail-header-subject (gnus-data-header
738 (assoc article (gnus-data-list nil))))))
23f87bed
MB
739 nil))
740
741(defun gnus-registry-fetch-sender-fast (article)
742 "Fetch the Sender quickly, using the internal gnus-data-list function"
743 (if (and (numberp article)
744 (assoc article (gnus-data-list nil)))
01c52d31
MB
745 (gnus-string-remove-all-properties
746 (mail-header-from (gnus-data-header
747 (assoc article (gnus-data-list nil)))))
23f87bed
MB
748 nil))
749
14e8de0c
MB
750(defun gnus-registry-do-marks (type function)
751 "For each known mark, call FUNCTION for each cell of type TYPE.
752
753FUNCTION should take two parameters, a mark symbol and the cell value."
754 (dolist (mark-info gnus-registry-marks)
8f7abae3
MB
755 (let* ((mark (car-safe mark-info))
756 (data (cdr-safe mark-info))
757 (cell-data (plist-get data type)))
758 (when cell-data
759 (funcall function mark cell-data)))))
14e8de0c
MB
760
761;;; this is ugly code, but I don't know how to do it better
8f7abae3 762(defun gnus-registry-install-shortcuts ()
14e8de0c
MB
763 "Install the keyboard shortcuts and menus for the registry.
764Uses `gnus-registry-marks' to find what shortcuts to install."
8f7abae3 765 (let (keys-plist)
ec7995fa
KY
766 (setq gnus-registry-misc-menus nil)
767 (gnus-registry-do-marks
8f7abae3
MB
768 :char
769 (lambda (mark data)
770 (let ((function-format
771 (format "gnus-registry-%%s-article-%s-mark" mark)))
14e8de0c
MB
772
773;;; The following generates these functions:
774;;; (defun gnus-registry-set-article-Important-mark (&rest articles)
775;;; "Apply the Important mark to process-marked ARTICLES."
776;;; (interactive (gnus-summary-work-articles current-prefix-arg))
777;;; (gnus-registry-set-article-mark-internal 'Important articles nil t))
778;;; (defun gnus-registry-remove-article-Important-mark (&rest articles)
779;;; "Apply the Important mark to process-marked ARTICLES."
780;;; (interactive (gnus-summary-work-articles current-prefix-arg))
781;;; (gnus-registry-set-article-mark-internal 'Important articles t t))
782
8f7abae3
MB
783 (dolist (remove '(t nil))
784 (let* ((variant-name (if remove "remove" "set"))
785 (function-name (format function-format variant-name))
786 (shortcut (format "%c" data))
787 (shortcut (if remove (upcase shortcut) shortcut)))
5e2a84e3 788 (unintern function-name obarray)
8f7abae3 789 (eval
c9fc72fa 790 `(defun
8f7abae3 791 ;; function name
c9fc72fa 792 ,(intern function-name)
8f7abae3
MB
793 ;; parameter definition
794 (&rest articles)
795 ;; documentation
c9fc72fa 796 ,(format
8f7abae3
MB
797 "%s the %s mark over process-marked ARTICLES."
798 (upcase-initials variant-name)
799 mark)
800 ;; interactive definition
c9fc72fa 801 (interactive
8f7abae3
MB
802 (gnus-summary-work-articles current-prefix-arg))
803 ;; actual code
804
805 ;; if this is called and the user doesn't want the
806 ;; registry enabled, we'll ask anyhow
807 (when (eq gnus-registry-install nil)
808 (setq gnus-registry-install 'ask))
809
810 ;; now the user is asked if gnus-registry-install is 'ask
811 (when (gnus-registry-install-p)
c9fc72fa 812 (gnus-registry-set-article-mark-internal
8f7abae3
MB
813 ;; all this just to get the mark, I must be doing it wrong
814 (intern ,(symbol-name mark))
815 articles ,remove t)
ec7995fa 816 (gnus-message
c9fc72fa 817 9
ec7995fa
KY
818 "Applying mark %s to %d articles"
819 ,(symbol-name mark) (length articles))
8f7abae3 820 (dolist (article articles)
c9fc72fa 821 (gnus-summary-update-article
ec7995fa 822 article
8f7abae3
MB
823 (assoc article (gnus-data-list nil)))))))
824 (push (intern function-name) keys-plist)
c9fc72fa 825 (push shortcut keys-plist)
ec7995fa
KY
826 (push (vector (format "%s %s"
827 (upcase-initials variant-name)
828 (symbol-name mark))
829 (intern function-name) t)
830 gnus-registry-misc-menus)
831 (gnus-message
c9fc72fa
LMI
832 9
833 "Defined mark handling function %s"
8f7abae3
MB
834 function-name))))))
835 (gnus-define-keys-1
ec7995fa
KY
836 '(gnus-registry-mark-map "M" gnus-summary-mark-map)
837 keys-plist)
838 (add-hook 'gnus-summary-menu-hook
839 (lambda ()
c9fc72fa 840 (easy-menu-add-item
ec7995fa 841 gnus-summary-misc-menu
c9fc72fa 842 nil
ec7995fa 843 (cons "Registry Marks" gnus-registry-misc-menus))))))
8f7abae3
MB
844
845;;; use like this:
c9fc72fa 846;;; (defalias 'gnus-user-format-function-M
8f7abae3
MB
847;;; 'gnus-registry-user-format-function-M)
848(defun gnus-registry-user-format-function-M (headers)
849 (let* ((id (mail-header-message-id headers))
850 (marks (when id (gnus-registry-fetch-extra-marks id))))
851 (apply 'concat (mapcar (lambda(mark)
c9fc72fa 852 (let ((c
8f7abae3 853 (plist-get
c9fc72fa 854 (cdr-safe
8f7abae3
MB
855 (assoc mark gnus-registry-marks))
856 :char)))
857 (if c
858 (list c)
859 nil)))
860 marks))))
0b6799c3
MB
861
862(defun gnus-registry-read-mark ()
863 "Read a mark name from the user with completion."
229b59da
G
864 (let ((mark (gnus-completing-read
865 "Label"
866 (mapcar 'symbol-name (mapcar 'car gnus-registry-marks))
867 nil nil nil
868 (symbol-name gnus-registry-default-mark))))
0b6799c3
MB
869 (when (stringp mark)
870 (intern mark))))
871
872(defun gnus-registry-set-article-mark (&rest articles)
873 "Apply a mark to process-marked ARTICLES."
874 (interactive (gnus-summary-work-articles current-prefix-arg))
875 (gnus-registry-set-article-mark-internal (gnus-registry-read-mark) articles nil t))
876
877(defun gnus-registry-remove-article-mark (&rest articles)
878 "Remove a mark from process-marked ARTICLES."
879 (interactive (gnus-summary-work-articles current-prefix-arg))
880 (gnus-registry-set-article-mark-internal (gnus-registry-read-mark) articles t t))
881
882(defun gnus-registry-set-article-mark-internal (mark articles &optional remove show-message)
883 "Apply a mark to a list of ARTICLES."
884 (let ((article-id-list
885 (mapcar 'gnus-registry-fetch-message-id-fast articles)))
886 (dolist (id article-id-list)
887 (let* (
888 ;; all the marks for this article without the mark of
889 ;; interest
890 (marks
891 (delq mark (gnus-registry-fetch-extra-marks id)))
892 ;; the new marks we want to use
893 (new-marks (if remove
894 marks
895 (cons mark marks))))
896 (when show-message
897 (gnus-message 1 "%s mark %s with message ID %s, resulting in %S"
898 (if remove "Removing" "Adding")
899 mark id new-marks))
c9fc72fa 900
0b6799c3
MB
901 (apply 'gnus-registry-store-extra-marks ; set the extra marks
902 id ; for the message ID
903 new-marks)))))
904
905(defun gnus-registry-get-article-marks (&rest articles)
906 "Get the Gnus registry marks for ARTICLES and show them if interactive.
907Uses process/prefix conventions. For multiple articles,
908only the last one's marks are returned."
909 (interactive (gnus-summary-work-articles 1))
910 (let (marks)
911 (dolist (article articles)
912 (let ((article-id
913 (gnus-registry-fetch-message-id-fast article)))
914 (setq marks (gnus-registry-fetch-extra-marks article-id))))
915 (when (interactive-p)
916 (gnus-message 1 "Marks are %S" marks))
917 marks))
918
919;;; if this extends to more than 'marks, it should be improved to be more generic.
920(defun gnus-registry-fetch-extra-marks (id)
921 "Get the marks of a message, based on the message ID.
922Returns a list of symbol marks or nil."
923 (car-safe (cdr (gnus-registry-fetch-extra id 'marks))))
924
925(defun gnus-registry-has-extra-mark (id mark)
926 "Checks if a message has `mark', based on the message ID `id'."
927 (memq mark (gnus-registry-fetch-extra-marks id)))
928
929(defun gnus-registry-store-extra-marks (id &rest mark-list)
930 "Set the marks of a message, based on the message ID.
931The `mark-list' can be nil, in which case no marks are left."
932 (gnus-registry-store-extra-entry id 'marks (list mark-list)))
933
934(defun gnus-registry-delete-extra-marks (id &rest mark-delete-list)
935 "Delete the message marks in `mark-delete-list', based on the message ID."
936 (let ((marks (gnus-registry-fetch-extra-marks id)))
937 (when marks
938 (dolist (mark mark-delete-list)
939 (setq marks (delq mark marks))))
940 (gnus-registry-store-extra-marks id (car marks))))
941
942(defun gnus-registry-delete-all-extra-marks (id)
943 "Delete all the marks for a message ID."
944 (gnus-registry-store-extra-marks id nil))
01c52d31 945
23f87bed
MB
946(defun gnus-registry-fetch-extra (id &optional entry)
947 "Get the extra data of a message, based on the message ID.
948Returns the first place where the trail finds a nonstring."
949 (let ((entry-cache (gethash entry gnus-registry-hashtb)))
950 (if (and entry
951 (hash-table-p entry-cache)
952 (gethash id entry-cache))
953 (gethash id entry-cache)
954 ;; else, if there is no caching possible...
955 (let ((trail (gethash id gnus-registry-hashtb)))
956 (when (listp trail)
957 (dolist (crumb trail)
958 (unless (stringp crumb)
959 (return (gnus-registry-fetch-extra-entry crumb entry id)))))))))
960
961(defun gnus-registry-fetch-extra-entry (alist &optional entry id)
962 "Get the extra data of a message, or a specific entry in it.
963Update the entry cache if needed."
964 (if (and entry id)
965 (let ((entry-cache (gethash entry gnus-registry-hashtb))
966 entree)
967 (when gnus-registry-entry-caching
968 ;; create the hash table
969 (unless (hash-table-p entry-cache)
970 (setq entry-cache (make-hash-table
971 :size 4096
972 :test 'equal))
973 (puthash entry entry-cache gnus-registry-hashtb))
974
975 ;; get the entree from the hash table or from the alist
976 (setq entree (gethash id entry-cache)))
bf247b6e 977
23f87bed
MB
978 (unless entree
979 (setq entree (assq entry alist))
980 (when gnus-registry-entry-caching
981 (puthash id entree entry-cache)))
982 entree)
983 alist))
984
985(defun gnus-registry-store-extra (id extra)
986 "Store the extra data of a message, based on the message ID.
987The message must have at least one group name."
988 (when (gnus-registry-group-count id)
989 ;; we now know the trail has at least 1 group name, so it's not empty
990 (let ((trail (gethash id gnus-registry-hashtb))
991 (old-extra (gnus-registry-fetch-extra id))
992 entry-cache)
993 (dolist (crumb trail)
994 (unless (stringp crumb)
995 (dolist (entry crumb)
996 (setq entry-cache (gethash (car entry) gnus-registry-hashtb))
997 (when entry-cache
998 (remhash id entry-cache))))
999 (puthash id (cons extra (delete old-extra trail))
1000 gnus-registry-hashtb)
1001 (setq gnus-registry-dirty t)))))
1002
01c52d31
MB
1003(defun gnus-registry-delete-extra-entry (id key)
1004 "Delete a specific entry in the extras field of the registry entry for id."
1005 (gnus-registry-store-extra-entry id key nil))
1006
23f87bed
MB
1007(defun gnus-registry-store-extra-entry (id key value)
1008 "Put a specific entry in the extras field of the registry entry for id."
1009 (let* ((extra (gnus-registry-fetch-extra id))
01c52d31 1010 ;; all the entries except the one for `key'
c9fc72fa 1011 (the-rest (gnus-assq-delete-all key (gnus-registry-fetch-extra id)))
01c52d31
MB
1012 (alist (if value
1013 (gnus-registry-remove-alist-text-properties
1014 (cons (cons key value)
1015 the-rest))
1016 the-rest)))
23f87bed
MB
1017 (gnus-registry-store-extra id alist)))
1018
1019(defun gnus-registry-fetch-group (id)
1020 "Get the group of a message, based on the message ID.
1021Returns the first place where the trail finds a group name."
1022 (when (gnus-registry-group-count id)
1023 ;; we now know the trail has at least 1 group name
1024 (let ((trail (gethash id gnus-registry-hashtb)))
1025 (dolist (crumb trail)
1026 (when (stringp crumb)
bf247b6e
KS
1027 (return (if gnus-registry-use-long-group-names
1028 crumb
23f87bed
MB
1029 (gnus-group-short-name crumb))))))))
1030
b86402ab
MB
1031(defun gnus-registry-fetch-groups (id &optional max)
1032 "Get the groups (up to MAX, if given) of a message, based on the message ID."
01c52d31
MB
1033 (let ((trail (gethash id gnus-registry-hashtb))
1034 groups)
1035 (dolist (crumb trail)
1036 (when (stringp crumb)
1037 ;; push the group name into the list
c9fc72fa 1038 (setq
01c52d31
MB
1039 groups
1040 (cons
1041 (if (or (not (stringp crumb)) gnus-registry-use-long-group-names)
1042 crumb
1043 (gnus-group-short-name crumb))
b86402ab
MB
1044 groups))
1045 (when (and max (> (length groups) max))
1046 (return))))
01c52d31
MB
1047 ;; return the list of groups
1048 groups))
1049
23f87bed
MB
1050(defun gnus-registry-group-count (id)
1051 "Get the number of groups of a message, based on the message ID."
1052 (let ((trail (gethash id gnus-registry-hashtb)))
1053 (if (and trail (listp trail))
1054 (apply '+ (mapcar (lambda (x) (if (stringp x) 1 0)) trail))
1055 0)))
1056
1057(defun gnus-registry-delete-group (id group)
1058 "Delete a group for a message, based on the message ID."
01c52d31 1059 (when (and group id)
23f87bed 1060 (let ((trail (gethash id gnus-registry-hashtb))
01c52d31 1061 (short-group (gnus-group-short-name group)))
23f87bed 1062 (puthash id (if trail
01c52d31 1063 (delete short-group (delete group trail))
23f87bed
MB
1064 nil)
1065 gnus-registry-hashtb))
1066 ;; now, clear the entry if there are no more groups
1067 (when gnus-registry-trim-articles-without-groups
1068 (unless (gnus-registry-group-count id)
1069 (gnus-registry-delete-id id)))
270a576a
MB
1070 ;; is this ID still in the registry?
1071 (when (gethash id gnus-registry-hashtb)
01c52d31 1072 (gnus-registry-store-extra-entry id 'mtime (current-time)))))
23f87bed
MB
1073
1074(defun gnus-registry-delete-id (id)
1075 "Delete a message ID from the registry."
1076 (when (stringp id)
1077 (remhash id gnus-registry-hashtb)
1078 (maphash
1079 (lambda (key value)
1080 (when (hash-table-p value)
1081 (remhash id value)))
1082 gnus-registry-hashtb)))
1083
1084(defun gnus-registry-add-group (id group &optional subject sender)
1085 "Add a group for a message, based on the message ID."
1086 (when group
1087 (when (and id
1088 (not (string-match "totally-fudged-out-message-id" id)))
1089 (let ((full-group group)
bf247b6e
KS
1090 (group (if gnus-registry-use-long-group-names
1091 group
23f87bed
MB
1092 (gnus-group-short-name group))))
1093 (gnus-registry-delete-group id group)
1094
1095 (unless gnus-registry-use-long-group-names ;; unnecessary in this case
1096 (gnus-registry-delete-group id full-group))
1097
1098 (let ((trail (gethash id gnus-registry-hashtb)))
1099 (puthash id (if trail
1100 (cons group trail)
1101 (list group))
1102 gnus-registry-hashtb)
1103
1104 (when (and (gnus-registry-track-subject-p)
1105 subject)
1106 (gnus-registry-store-extra-entry
bf247b6e
KS
1107 id
1108 'subject
23f87bed
MB
1109 (gnus-registry-simplify-subject subject)))
1110 (when (and (gnus-registry-track-sender-p)
1111 sender)
1112 (gnus-registry-store-extra-entry
bf247b6e 1113 id
23f87bed
MB
1114 'sender
1115 sender))
bf247b6e 1116
23f87bed
MB
1117 (gnus-registry-store-extra-entry id 'mtime (current-time)))))))
1118
1119(defun gnus-registry-clear ()
1120 "Clear the Gnus registry."
1121 (interactive)
1122 (setq gnus-registry-alist nil)
996aa8c1 1123 (setq gnus-registry-hashtb (gnus-alist-to-hashtable gnus-registry-alist))
23f87bed
MB
1124 (setq gnus-registry-dirty t))
1125
1126;;;###autoload
1127(defun gnus-registry-initialize ()
8f7abae3 1128"Initialize the Gnus registry."
23f87bed 1129 (interactive)
8f7abae3
MB
1130 (gnus-message 5 "Initializing the registry")
1131 (setq gnus-registry-install t) ; in case it was 'ask or nil
23f87bed 1132 (gnus-registry-install-hooks)
8f7abae3 1133 (gnus-registry-install-shortcuts)
a3f57c41
G
1134 (when (featurep 'nnregistry)
1135 (gnus-registry-install-nnregistry))
23f87bed
MB
1136 (gnus-registry-read))
1137
1138;;;###autoload
1139(defun gnus-registry-install-hooks ()
1140 "Install the registry hooks."
1141 (interactive)
bf247b6e 1142 (add-hook 'gnus-summary-article-move-hook 'gnus-registry-action)
23f87bed
MB
1143 (add-hook 'gnus-summary-article-delete-hook 'gnus-registry-action)
1144 (add-hook 'gnus-summary-article-expire-hook 'gnus-registry-action)
1145 (add-hook 'nnmail-spool-hook 'gnus-registry-spool-action)
bf247b6e 1146
23f87bed
MB
1147 (add-hook 'gnus-save-newsrc-hook 'gnus-registry-save)
1148 (add-hook 'gnus-read-newsrc-el-hook 'gnus-registry-read)
1149
1150 (add-hook 'gnus-summary-prepare-hook 'gnus-registry-register-message-ids))
1151
6b958814
G
1152;;;###autoload
1153(defun gnus-registry-install-nnregistry ()
1154 "Install the nnregistry refer method in `gnus-refer-article-method'."
1155 (interactive)
1225bc49
KY
1156 (cond ((eq 'nnregistry gnus-refer-article-method))
1157 ((null gnus-refer-article-method)
1158 (setq gnus-refer-article-method 'nnregistry))
1159 ((consp gnus-refer-article-method)
1160 (unless (memq 'nnregistry gnus-refer-article-method)
1161 (setq gnus-refer-article-method
1162 (append gnus-refer-article-method '(nnregistry)))))
1163 (t
1164 (setq gnus-refer-article-method
1165 (list gnus-refer-article-method 'nnregistry)))))
6b958814 1166
23f87bed
MB
1167(defun gnus-registry-unload-hook ()
1168 "Uninstall the registry hooks."
1169 (interactive)
bf247b6e 1170 (remove-hook 'gnus-summary-article-move-hook 'gnus-registry-action)
23f87bed
MB
1171 (remove-hook 'gnus-summary-article-delete-hook 'gnus-registry-action)
1172 (remove-hook 'gnus-summary-article-expire-hook 'gnus-registry-action)
1173 (remove-hook 'nnmail-spool-hook 'gnus-registry-spool-action)
bf247b6e 1174
23f87bed
MB
1175 (remove-hook 'gnus-save-newsrc-hook 'gnus-registry-save)
1176 (remove-hook 'gnus-read-newsrc-el-hook 'gnus-registry-read)
1177
1178 (remove-hook 'gnus-summary-prepare-hook 'gnus-registry-register-message-ids))
1179
6d52545d
RS
1180(add-hook 'gnus-registry-unload-hook 'gnus-registry-unload-hook)
1181
8f7abae3
MB
1182(defun gnus-registry-install-p ()
1183 (interactive)
1184 (when (eq gnus-registry-install 'ask)
1185 (setq gnus-registry-install
1186 (gnus-y-or-n-p
1187 (concat "Enable the Gnus registry? "
1188 "See the variable `gnus-registry-install' "
1189 "to get rid of this query permanently. ")))
1190 (when gnus-registry-install
1191 ;; we just set gnus-registry-install to t, so initialize the registry!
1192 (gnus-registry-initialize)))
1193;;; we could call it here: (customize-variable 'gnus-registry-install)
1194 gnus-registry-install)
1195
8f7abae3 1196;; TODO: a few things
23f87bed
MB
1197
1198(provide 'gnus-registry)
1199
23f87bed 1200;;; gnus-registry.el ends here