Mercurial > hg > xemacs-beta
diff lisp/gnus/gnus-edit.el @ 0:376386a54a3c r19-14
Import from CVS: tag r19-14
author | cvs |
---|---|
date | Mon, 13 Aug 2007 08:45:50 +0200 |
parents | |
children | ac2d302a0011 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/gnus/gnus-edit.el Mon Aug 13 08:45:50 2007 +0200 @@ -0,0 +1,630 @@ +;;; gnus-edit.el --- Gnus SCORE file editing +;; Copyright (C) 1995,96 Free Software Foundation, Inc. +;; +;; Author: Per Abrahamsen <abraham@iesd.auc.dk> +;; Keywords: news, help +;; Version: 0.2 + +;;; Commentary: +;; +;; Type `M-x gnus-score-customize RET' to invoke. + +;;; Code: + +(require 'custom) +(require 'gnus-score) +(eval-when-compile (require 'cl)) + +(defconst gnus-score-custom-data + '((tag . "Score") + (doc . "Customization of Gnus SCORE files. + +SCORE files allow you to assign a score to each article when you enter +a group, and automatically mark the articles as read or delete them +based on the score. In the summary buffer you can use the score to +sort the articles by score (`C-c C-s C-s') or to jump to the unread +article with the highest score (`,').") + (type . group) + (data "\n" + ((header . nil) + (doc . "Name of SCORE file to customize. + +Enter the name in the `File' field, then push the [Load] button to +load it. When done editing, push the [Save] button to save the file. + +Several score files may apply to each group, and several groups may +use the same score file. This is controlled implicitly by the name of +the score file and the value of the global variable +`gnus-score-find-score-files-function', and explicitly by the the +`Files' and `Exclude Files' entries.") + (compact . t) + (type . group) + (data ((tag . "Load") + (type . button) + (query . gnus-score-custom-load)) + ((tag . "Save") + (type . button) + (query . gnus-score-custom-save)) + ((name . file) + (tag . "File") + (directory . gnus-kill-files-directory) + (default-file . "SCORE") + (type . file)))) + ((name . files) + (tag . "Files") + (doc . "\ +List of score files to load when the the current score file is loaded. +You can use this to share score entries between multiple score files. + +Push the `[INS]' button add a score file to the list, or `[DEL]' to +delete a score file from the list.") + (type . list) + (data ((type . repeat) + (header . nil) + (data (type . file) + (directory . gnus-kill-files-directory))))) + ((name . exclude-files) + (tag . "Exclude Files") + (doc . "\ +List of score files to exclude when the the current score file is loaded. +You can use this if you have a score file you want to share between a +number of newsgroups, except for the newsgroup this score file +matches. [ Did anyone get that? ] + +Push the `[INS]' button add a score file to the list, or `[DEL]' to +delete a score file from the list.") + (type . list) + (data ((type . repeat) + (header . nil) + (data (type . file) + (directory . gnus-kill-files-directory))))) + ((name . mark) + (tag . "Mark") + (doc . "\ +Articles below this score will be automatically marked as read. + +This means that when you enter the summary buffer, the articles will +be shown but will already be marked as read. You can then press `x' +to get rid of them entirely. + +By default articles with a negative score will be marked as read. To +change this, push the `Mark' button, and choose `Integer'. You can +then enter a value in the `Mark' field.") + (type . gnus-score-custom-maybe-type)) + ((name . expunge) + (tag . "Expunge") + (doc . "\ +Articles below this score will not be shown in the summary buffer.") + (type . gnus-score-custom-maybe-type)) + ((name . mark-and-expunge) + (tag . "Mark and Expunge") + (doc . "\ +Articles below this score will be marked as read, but not shown. + +Someone should explain me the difference between this and `expunge' +alone or combined with `mark'.") + (type . gnus-score-custom-maybe-type)) + ((name . eval) + (tag . "Eval") + (doc . "\ +Evaluate this lisp expression when the entering summary buffer.") + (type . sexp)) + ((name . read-only) + (tag . "Read Only") + (doc . "Read-only score files will not be updated or saved. +Except from this buffer, of course!") + (type . toggle)) + ((type . doc) + (doc . "\ +Each news header has an associated list of score entries. +You can use the [INS] buttons to add new score entries anywhere in the +list, or the [DEL] buttons to delete specific score entries. + +Each score entry should specify a string that should be matched with +the content actual header in order to determine whether the entry +applies to that header. Enter that string in the `Match' field. + +If the score entry matches, the articles score will be adjusted with +some amount. Enter that amount in the in the `Score' field. You +should specify a positive amount for score entries that matches +articles you find interesting, and a negative amount for score entries +matching articles you would rather avoid. The final score for the +article will be the sum of the score of all score entries that match +the article. + +The score entry can be either permanent or expirable. To make the +entry permanent, push the `Date' button and choose the `Permanent' +entry. To make the entry expirable, choose instead the `Integer' +entry. After choosing the you can enter the date the score entry was +last matched in the `Date' field. The date will be automatically +updated each time the score entry matches an article. When the date +become too old, the the score entry will be removed. + +For your convenience, the date is specified as the number of days +elapsed since the (imaginary) Gregorian date Sunday, December 31, 1 +BC. + +Finally, you can choose what kind of match you want to perform by +pushing the `Type' button. For most entries you can choose between +`Exact' which mean the header content must be exactly identical to the +match string, or `Substring' meaning the match string should be +somewhere in the header content, or even `Regexp' to use Emacs regular +expression matching. The last choice is `Fuzzy' which is like `Exact' +except that whitespace derivations, a beginning `Re:' or a terminating +parenthetical remark are all ignored. Each of the four types have a +variant which will ignore case in the comparison. That variant is +indicated with a `(fold)' after its name.")) + ((name . from) + (tag . "From") + (doc . "Scoring based on the authors email address.") + (type . gnus-score-custom-string-type)) + ((name . subject) + (tag . "Subject") + (doc . "Scoring based on the articles subject.") + (type . gnus-score-custom-string-type)) + ((name . followup) + (tag . "Followup") + (doc . "Scoring based on who the article is a followup to. + +If you want to see all followups to your own articles, add an entry +with a positive score matching your email address here. You can also +put an entry with a negative score matching someone who is so annoying +that you don't even want to see him quoted in followups.") + (type . gnus-score-custom-string-type)) + ((name . xref) + (tag . "Xref") + (doc . "Scoring based on article crossposting. + +If you want to score based on which newsgroups an article is posted +to, this is the header to use. The syntax is a little different from +the `Newsgroups' header, but scoring in `Xref' is much faster. As an +example, to match all crossposted articles match on `:.*:' using the +`Regexp' type.") + (type . gnus-score-custom-string-type)) + ((name . references) + (tag . "References") + (doc . "Scoring based on article references. + +The `References' header gives you an alternative way to score on +followups. If you for example want to see follow all discussions +where people from `iesd.auc.dk' school participate, you can add a +substring match on `iesd.auc.dk>' on this header.") + (type . gnus-score-custom-string-type)) + ((name . message-id) + (tag . "Message-ID") + (doc . "Scoring based on the articles message-id. + +This isn't very useful, but Lars like completeness. You can use it to +match all messaged generated by recent Gnus version with a `Substring' +match on `.fsf@'.") + (type . gnus-score-custom-string-type)) + ((type . doc) + (doc . "\ +WARNING: Scoring on the following three pseudo headers is very slow! +Scoring on any of the real headers use a technique that avoids +scanning the entire article, only the actual headers you score on are +scanned, and this scanning has been heavily optimized. Using just a +single entry for one the three pseudo-headers `Head', `Body', and +`All' will require GNUS to retrieve and scan the entire article, which +can be very slow on large groups. However, if you add one entry for +any of these headers, you can just as well add several. Each +subsequent entry cost relatively little extra time.")) + ((name . head) + (tag . "Head") + (doc . "Scoring based on the article header. + +Instead of matching the content of a single header, the entire header +section of the article is matched. You can use this to match on +arbitrary headers, foe example to single out TIN lusers, use a substring +match on `Newsreader: TIN'. That should get 'em!") + (type . gnus-score-custom-string-type)) + ((name . body) + (tag . "Body") + (doc . "Scoring based on the article body. + +If you think any article that mentions `Kibo' is inherently +interesting, do a substring match on His name. You Are Allowed.") + (type . gnus-score-custom-string-type)) + ((name . all) + (tag . "All") + (doc . "Scoring based on the whole article.") + (type . gnus-score-custom-string-type)) + ((name . date) + (tag . "Date") + (doc . "Scoring based on article date. + +You can change the score of articles that have been posted before, +after, or at a specific date. You should add the date in the `Match' +field, and then select `before', `after', or `at' by pushing the +`Type' button. Imagine you want to lower the score of very old +articles, or want to raise the score of articles from the future (such +things happen!). Then you can't use date scoring for that. In fact, +I can't imagine anything you would want to use this for. + +For your convenience, the date is specified in Usenet date format.") + (type . gnus-score-custom-date-type)) + ((type . doc) + (doc . "\ +The Lines and Chars headers use integer based scoring. + +This means that you should write an integer in the `Match' field, and +the push the `Type' field to if the `Chars' or `Lines' header should +be larger, equal, or smaller than the number you wrote in the match +field.")) + ((name . chars) + (tag . "Characters") + (doc . "Scoring based on the number of characters in the article.") + (type . gnus-score-custom-integer-type)) + ((name . lines) + (tag . "Lines") + (doc . "Scoring based on the number of lines in the article.") + (type . gnus-score-custom-integer-type)) + ((name . orphan) + (tag . "Orphan") + (doc . "Score to add to articles with no parents.") + (type . gnus-score-custom-maybe-type)) + ((name . adapt) + (tag . "Adapt") + (doc . "Adapting the score files to your newsreading habits. + +When you have finished reading a group GNUS can automatically create +new score entries based on which articles you read and which you +skipped. This is normally controlled by the two global variables +`gnus-use-adaptive-scoring' and `gnus-default-adaptive-score-alist', +The first determines whether adaptive scoring should be enabled or +not, while the second determines what score entries should be created. + +You can overwrite the setting of `gnus-use-adaptive-scoring' by +selecting `Enable' or `Disable' by pressing the `Adapt' button. +Selecting `Custom' will allow you to specify the exact adaptation +rules (overwriting `gnus-default-adaptive-score-alist').") + (type . choice) + (data ((tag . "Default") + (default . nil) + (type . const)) + ((tag . "Enable") + (default . t) + (type . const)) + ((tag . "Disable") + (default . ignore) + (type . const)) + ((tag . "Custom") + (doc . "Customization of adaptive scoring. + +Each time you read an article it will be marked as read. Likewise, if +you delete it it will be marked as deleted, and if you tick it it will +be marked as ticked. When you leave a group, GNUS can automatically +create score file entries based on these marks, so next time you enter +the group articles with subjects that you read last time have higher +score and articles with subjects that deleted will have lower score. + +Below is a list of such marks. You can insert new marks to the list +by pushing on one of the `[INS]' buttons in the left margin to create +a new entry and then pushing the `Mark' button to select the mark. +For each mark there is another list, this time of article headers, +which determine how the mark should affect that header. The `[INS]' +buttons of this list are indented to indicate that the belong to the +mark above. Push the `Header' button to choose a header, and then +enter a score value in the `Score' field. + +For each article that are marked with `Mark' when you leave the +group, a temporary score entry for the articles `Header' with the +value of `Score' will be added the adapt file. If the score entry +already exists, `Score' will be added to its value. If you understood +that, you are smart. + +You can select the special value `Other' when pressing the `Mark' or +`Header' buttons. This is because Lars might add more useful values +there. If he does, it is up to you to figure out what they are named.") + (type . list) + (default . ((__uninitialized__))) + (data ((type . repeat) + (header . nil) + (data . ((type . list) + (header . nil) + (compact . t) + (data ((type . choice) + (tag . "Mark") + (data ((tag . "Unread") + (default . gnus-unread-mark) + (type . const)) + ((tag . "Ticked") + (default . gnus-ticked-mark) + (type . const)) + ((tag . "Dormant") + (default . gnus-dormant-mark) + (type . const)) + ((tag . "Deleted") + (default . gnus-del-mark) + (type . const)) + ((tag . "Read") + (default . gnus-read-mark) + (type . const)) + ((tag . "Expirable") + (default . gnus-expirable-mark) + (type . const)) + ((tag . "Killed") + (default . gnus-killed-mark) + (type . const)) + ((tag . "Kill-file") + (default . gnus-kill-file-mark) + (type . const)) + ((tag . "Low-score") + (default . gnus-low-score-mark) + (type . const)) + ((tag . "Catchup") + (default . gnus-catchup-mark) + (type . const)) + ((tag . "Ancient") + (default . gnus-ancient-mark) + (type . const)) + ((tag . "Canceled") + (default . gnus-canceled-mark) + (type . const)) + ((prompt . "Other") + (default . ??) + (type . sexp)))) + ((type . repeat) + (prefix . " ") + (data . ((type . list) + (compact . t) + (data ((tag . "Header") + (type . choice) + (data ((tag . "Subject") + (default . subject) + (type . const)) + ((prompt . "From") + (tag . "From ") + (default . from) + (type . const)) + ((prompt . "Other") + (width . 7) + (default . nil) + (type . symbol)))) + ((tag . "Score") + (type . integer)))))))))))))) + ((name . local) + (tag . "Local") + (doc . "\ +List of local variables to set when this score file is loaded. + +Using this entry can provide a convenient way to set variables that +will affect the summary mode for only some specific groups, i.e. those +groups matched by the current score file.") + (type . list) + (data ((type . repeat) + (header . nil) + (data . ((type . list) + (compact . t) + (data ((tag . "Name") + (width . 26) + (type . symbol)) + ((tag . "Value") + (width . 26) + (type . sexp))))))))))) + +(defconst gnus-score-custom-type-properties + '((gnus-score-custom-maybe-type + (type . choice) + (data ((type . integer) + (default . 0)) + ((tag . "Default") + (type . const) + (default . nil)))) + (gnus-score-custom-string-type + (type . list) + (data ((type . repeat) + (header . nil) + (data . ((type . list) + (compact . t) + (data ((tag . "Match") + (width . 59) + (type . string)) + "\n " + ((tag . "Score") + (type . integer)) + ((tag . "Date") + (type . choice) + (data ((type . integer) + (default . 0) + (width . 9)) + ((tag . "Permanent") + (type . const) + (default . nil)))) + ((tag . "Type") + (type . choice) + (data ((tag . "Exact") + (default . E) + (type . const)) + ((tag . "Substring") + (default . S) + (type . const)) + ((tag . "Regexp") + (default . R) + (type . const)) + ((tag . "Fuzzy") + (default . F) + (type . const)) + ((tag . "Exact (fold)") + (default . e) + (type . const)) + ((tag . "Substring (fold)") + (default . s) + (type . const)) + ((tag . "Regexp (fold)") + (default . r) + (type . const)) + ((tag . "Fuzzy (fold)") + (default . f) + (type . const)))))))))) + (gnus-score-custom-integer-type + (type . list) + (data ((type . repeat) + (header . nil) + (data . ((type . list) + (compact . t) + (data ((tag . "Match") + (type . integer)) + ((tag . "Score") + (type . integer)) + ((tag . "Date") + (type . choice) + (data ((type . integer) + (default . 0) + (width . 9)) + ((tag . "Permanent") + (type . const) + (default . nil)))) + ((tag . "Type") + (type . choice) + (data ((tag . "<") + (default . <) + (type . const)) + ((tag . ">") + (default . >) + (type . const)) + ((tag . "=") + (default . =) + (type . const)) + ((tag . ">=") + (default . >=) + (type . const)) + ((tag . "<=") + (default . <=) + (type . const)))))))))) + (gnus-score-custom-date-type + (type . list) + (data ((type . repeat) + (header . nil) + (data . ((type . list) + (compact . t) + (data ((tag . "Match") + (width . 59) + (type . string)) + "\n " + ((tag . "Score") + (type . integer)) + ((tag . "Date") + (type . choice) + (data ((type . integer) + (default . 0) + (width . 9)) + ((tag . "Permanent") + (type . const) + (default . nil)))) + ((tag . "Type") + (type . choice) + (data ((tag . "Before") + (default . before) + (type . const)) + ((tag . "After") + (default . after) + (type . const)) + ((tag . "At") + (default . at) + (type . const)))))))))))) + +(defvar gnus-score-custom-file nil + "Name of SCORE file being customized.") + +(defun gnus-score-customize () + "Create a buffer for editing gnus SCORE files." + (interactive) + (let (gnus-score-alist) + (custom-buffer-create "*Score Edit*" gnus-score-custom-data + gnus-score-custom-type-properties + 'gnus-score-custom-set + 'gnus-score-custom-get + 'gnus-score-custom-save)) + (make-local-variable 'gnus-score-custom-file) + (setq gnus-score-custom-file + (expand-file-name "SCORE" gnus-kill-files-directory)) + (make-local-variable 'gnus-score-alist) + (setq gnus-score-alist nil) + (custom-reset-all)) + +(defun gnus-score-custom-get (name) + (if (eq name 'file) + gnus-score-custom-file + (let ((entry (assoc (symbol-name name) gnus-score-alist))) + (if entry + (mapcar 'gnus-score-custom-sanify (cdr entry)) + (setq entry (assoc name gnus-score-alist)) + (if (or (memq name '(files exclude-files local)) + (and (eq name 'adapt) + (not (symbolp (car (cdr entry)))))) + (cdr entry) + (car (cdr entry))))))) + +(defun gnus-score-custom-set (name value) + (cond ((eq name 'file) + (setq gnus-score-custom-file value)) + ((assoc (symbol-name name) gnus-score-alist) + (if value + (setcdr (assoc (symbol-name name) gnus-score-alist) value) + (setq gnus-score-alist (delq (assoc (symbol-name name) + gnus-score-alist) + gnus-score-alist)))) + ((assoc (symbol-name name) gnus-header-index) + (if value + (setq gnus-score-alist + (cons (cons (symbol-name name) value) gnus-score-alist)))) + ((assoc name gnus-score-alist) + (cond ((null value) + (setq gnus-score-alist (delq (assoc name gnus-score-alist) + gnus-score-alist))) + ((and (listp value) (not (eq name 'eval))) + (setcdr (assoc name gnus-score-alist) value)) + (t + (setcdr (assoc name gnus-score-alist) (list value))))) + ((null value)) + ((and (listp value) (not (eq name 'eval))) + (setq gnus-score-alist (cons (cons name value) gnus-score-alist))) + (t + (setq gnus-score-alist + (cons (cons name (list value)) gnus-score-alist))))) + +(defun gnus-score-custom-sanify (entry) + (list (nth 0 entry) + (or (nth 1 entry) gnus-score-interactive-default-score) + (nth 2 entry) + (cond ((null (nth 3 entry)) + 's) + ((memq (nth 3 entry) '(before after at >= <=)) + (nth 3 entry)) + (t + (intern (substring (symbol-name (nth 3 entry)) 0 1)))))) + +(defvar gnus-score-cache nil) + +(defun gnus-score-custom-load () + (interactive) + (let ((file (custom-name-value 'file))) + (if (eq file custom-nil) + (error "You must specify a file name")) + (setq file (expand-file-name file gnus-kill-files-directory)) + (gnus-score-load file) + (setq gnus-score-custom-file file) + (custom-reset-all) + (gnus-message 4 "Loaded"))) + +(defun gnus-score-custom-save () + (interactive) + (custom-apply-all) + (gnus-score-remove-from-cache gnus-score-custom-file) + (let ((file gnus-score-custom-file) + (score gnus-score-alist) + emacs-lisp-mode-hook) + (save-excursion + (set-buffer (get-buffer-create "*Score*")) + (buffer-disable-undo (current-buffer)) + (erase-buffer) + (pp score (current-buffer)) + (gnus-make-directory (file-name-directory file)) + (write-region (point-min) (point-max) file nil 'silent) + (kill-buffer (current-buffer)))) + (gnus-message 4 "Saved")) + +(provide 'gnus-edit) + +;;; gnus-edit.el end here