*** empty log message ***
[gnus] / lisp / gnus-score.el
index 9732f6f..98c6ed6 100644 (file)
@@ -108,7 +108,7 @@ If this variable is nil, then score entries that provide matches
 will be expired along with non-matching score entries.")
 
 (defvar gnus-orphan-score nil
-  "*All orphans get this score added. Set in the score file.")
+  "*All orphans get this score added.  Set in the score file.")
 
 (defvar gnus-decay-scores nil
   "*If non-nil, decay non-permanent scores.")
@@ -124,7 +124,7 @@ It is called with one parameter -- the score to be decayed.")
   "*Decay all \"big\" scores with this factor.")
 
 (defvar gnus-home-score-file nil
-  "Variable to control where interative score entries are to go.
+  "Variable to control where interactive score entries are to go.
 It can be:
 
  * A string
@@ -187,14 +187,6 @@ This variable allows the same syntax as `gnus-home-score-file'.")
     "re")
   "Default list of words to be ignored when doing adaptive word scoring.")
 
-(defvar gnus-adaptive-word-syntax-table
-  (let ((table (copy-syntax-table (standard-syntax-table)))
-       (numbers '(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)))
-    (while numbers
-      (modify-syntax-entry (pop numbers) " " table))
-    table)
-  "Syntax table used when doing adaptive word scoring.")
-
 (defvar gnus-default-adaptive-word-score-alist  
   `((,gnus-read-mark . 30)
     (,gnus-catchup-mark . -10)
@@ -274,6 +266,15 @@ If nil, the user will be asked for a duration.")
 
 ;; Internal variables.
 
+(defvar gnus-adaptive-word-syntax-table
+  (let ((table (copy-syntax-table (standard-syntax-table)))
+       (numbers '(?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9)))
+    (while numbers
+      (modify-syntax-entry (pop numbers) " " table))
+    (modify-syntax-entry ?' "w" table)
+    table)
+  "Syntax table used when doing adaptive word scoring.")
+
 (defvar gnus-scores-exclude-files nil)
 (defvar gnus-internal-global-score-files nil)
 (defvar gnus-score-file-list nil)
@@ -282,6 +283,7 @@ If nil, the user will be asked for a duration.")
 
 (defvar gnus-score-help-winconf nil)
 (defvar gnus-adaptive-score-alist gnus-default-adaptive-score-alist)
+(defvar gnus-adaptive-word-score-alist gnus-default-adaptive-word-score-alist)
 (defvar gnus-score-trace nil)
 (defvar gnus-score-edit-buffer nil)
 
@@ -340,8 +342,7 @@ of the last successful match.")
   "f" gnus-score-edit-file
   "F" gnus-score-flush-cache
   "t" gnus-score-find-trace
-  "w" gnus-score-find-favourite-words
-  "C" gnus-score-customize)
+  "w" gnus-score-find-favourite-words)
 
 ;; Summary score file commands
 
@@ -440,7 +441,7 @@ used as score."
            (if mimic (error "%c %c" prefix hchar) (error "")))
 
          (when (/= (downcase hchar) hchar)
-           ;; This was a majuscle, so we end reading and set the defaults.
+           ;; This was a majuscule, so we end reading and set the defaults.
            (if mimic (message "%c %c" prefix hchar) (message ""))
            (setq tchar (or tchar ?s)
                  pchar (or pchar ?t)))
@@ -477,7 +478,7 @@ used as score."
            (if mimic (error "%c %c" prefix hchar) (error "")))
 
          (when (/= (downcase tchar) tchar)
-           ;; It was a majuscle, so we end reading and use the default.
+           ;; It was a majuscule, so we end reading and use the default.
            (if mimic (message "%c %c %c" prefix hchar tchar)
              (message ""))
            (setq pchar (or pchar ?p)))
@@ -527,7 +528,7 @@ used as score."
      (if (eq 's score) nil score)      ; Score
      (if (eq 'perm temporary)          ; Temp
         nil
-        temporary)
+       temporary)
      (not (nth 3 entry)))              ; Prompt
     ))
   
@@ -550,7 +551,7 @@ used as score."
            (setq max n))
        (setq list (cdr list)))
       (setq max (+ max 4))             ; %c, `:', SPACE, a SPACE at end
-      (setq n (/ (1- (window-width)) max))     ; items per line
+      (setq n (/ (1- (window-width)) max)) ; items per line
       (setq width (/ (1- (window-width)) n)) ; width of each item
       ;; insert `n' items, each in a field of width `width' 
       (while alist
@@ -662,7 +663,7 @@ If optional argument `SILENT' is nil, show effect of score entry."
     (unless (eq date 'now)
       ;; Add the score entry to the score file.
       (when (= score gnus-score-interactive-default-score)
-          (setq score nil))
+       (setq score nil))
       (let ((old (gnus-score-get header))
            elem)
        (setq new
@@ -677,7 +678,7 @@ If optional argument `SILENT' is nil, show effect of score entry."
               (t (list match))))
        ;; We see whether we can collapse some score entries.
        ;; This isn't quite correct, because there may be more elements
-       ;; later on with the same key that have matching elems... Hm.
+       ;; later on with the same key that have matching elems...  Hm.
        (if (and old
                 (setq elem (assoc match old))
                 (eq (nth 3 elem) (nth 3 new))
@@ -900,7 +901,7 @@ SCORE is the score to add."
   (interactive (list gnus-current-score-file))
   (let ((winconf (current-window-configuration)))
     (and (buffer-name gnus-summary-buffer) (gnus-score-save))
-    (make-directory (file-name-directory file) t)
+    (gnus-make-directory (file-name-directory file))
     (setq gnus-score-edit-buffer (find-file-noselect file))
     (gnus-configure-windows 'edit-score)
     (gnus-score-mode)
@@ -915,7 +916,7 @@ SCORE is the score to add."
   "Edit a score file."
   (interactive 
    (list (read-file-name "Edit score file: " gnus-kill-files-directory)))
-  (make-directory (file-name-directory file) t)
+  (gnus-make-directory (file-name-directory file))
   (and (buffer-name gnus-summary-buffer) (gnus-score-save))
   (let ((winconf (current-window-configuration)))
     (setq gnus-score-edit-buffer (find-file-noselect file))
@@ -984,7 +985,8 @@ SCORE is the score to add."
          (eval (car (gnus-score-get 'eval alist))))
       ;; Perform possible decays.
       (when (and gnus-decay-scores
-                (gnus-decay-scores alist decay))
+                (gnus-decay-scores 
+                 alist (or decay (gnus-time-to-day (current-time)))))
        (gnus-score-set 'touched '(t) alist)
        (gnus-score-set 'decay (list (gnus-time-to-day (current-time)))))
       ;; We do not respect eval and files atoms from global score
@@ -1160,7 +1162,7 @@ SCORE is the score to add."
        entry score file)
     (save-excursion
       (setq gnus-score-alist nil)
-      (nnheader-set-temp-buffer "*Score*")
+      (nnheader-set-temp-buffer " *Gnus Scores*")
       (while cache
        (current-buffer)
        (setq entry (pop cache)
@@ -1184,21 +1186,18 @@ SCORE is the score to add."
              ;; This is a normal score file, so we print it very
              ;; prettily. 
              (pp score (current-buffer))))
-         (if (and (not (file-exists-p (file-name-directory file)))
-                  (make-directory (file-name-directory file) t))
-             (gnus-error 1 "Can't create directory %s"
-                         (file-name-directory file))
-           ;; If the score file is empty, we delete it.
-           (if (zerop (buffer-size))
-               (delete-file file)
-             ;; There are scores, so we write the file. 
-             (when (file-writable-p file)
-               (write-region (point-min) (point-max) file nil 'silent)
-               (when gnus-score-after-write-file-function
-                 (funcall gnus-score-after-write-file-function file)))))
-         (and gnus-score-uncacheable-files
-              (string-match gnus-score-uncacheable-files file)
-              (gnus-score-remove-from-cache file))))
+         (gnus-make-directory (file-name-directory file))
+         ;; If the score file is empty, we delete it.
+         (if (zerop (buffer-size))
+             (delete-file file)
+           ;; There are scores, so we write the file. 
+           (when (file-writable-p file)
+             (write-region (point-min) (point-max) file nil 'silent)
+             (when gnus-score-after-write-file-function
+               (funcall gnus-score-after-write-file-function file)))))
+       (and gnus-score-uncacheable-files
+            (string-match gnus-score-uncacheable-files file)
+            (gnus-score-remove-from-cache file)))
       (kill-buffer (current-buffer)))))
 
 (defun gnus-score-load-files (score-files)
@@ -1358,7 +1357,7 @@ SCORE is the score to add."
             new-thread-ids (cdr new-thread-ids))
       (goto-char (point-min))
       (while (search-forward this-id nil t)
-        ;; found a match. remove this line
+        ;; found a match.  remove this line
        (beginning-of-line)
        (kill-line 1)))
 
@@ -1404,8 +1403,8 @@ SCORE is the score to add."
          ;; time than one would gain.
          (while articles
            (when (funcall match-func 
-                         (or (aref (caar articles) gnus-score-index) 0)
-                         match)
+                          (or (aref (caar articles) gnus-score-index) 0)
+                          match)
              (when trace 
                (push (cons (car-safe (rassq alist gnus-score-cache)) kill)
                      gnus-score-trace))
@@ -1475,7 +1474,7 @@ SCORE is the score to add."
                ((and found gnus-update-score-entry-dates) ;Match, update date.
                 (gnus-score-set 'touched '(t) alist)
                 (setcar (nthcdr 2 kill) now))
-               ((and expire (< date expire))   ;Old entry, remove.
+               ((and expire (< date expire)) ;Old entry, remove.
                 (gnus-score-set 'touched '(t) alist)
                 (setcdr entries (cdr rest))
                 (setq rest entries)))
@@ -1673,7 +1672,7 @@ SCORE is the score to add."
                ((and found gnus-update-score-entry-dates) ;Match, update date.
                 (gnus-score-set 'touched '(t) alist)
                 (setcar (nthcdr 2 kill) now))
-               ((and expire (< date expire))   ;Old entry, remove.
+               ((and expire (< date expire)) ;Old entry, remove.
                 (gnus-score-set 'touched '(t) alist)
                 (setcdr entries (cdr rest))
                 (setq rest entries)))
@@ -1711,7 +1710,7 @@ SCORE is the score to add."
   ;; Insert the unique article headers in the buffer.
   (let ((gnus-score-index (nth 1 (assoc header gnus-header-index)))
        ;; gnus-score-index is used as a free variable.
-       alike last this art entries alist articles scores 
+       alike last this art entries alist articles 
        fuzzies arts words kill)
 
     ;; Sorting the articles costs os O(N*log N) but will allow us to
@@ -1921,7 +1920,7 @@ SCORE is the score to add."
   ;; Find all the words in the buffer and enter them into
   ;; the hashtable.
   (let ((syntab (syntax-table))
-        word val)
+       word val)
     (goto-char (point-min))
     (unwind-protect
        (progn
@@ -2039,10 +2038,12 @@ SCORE is the score to add."
                (set-syntax-table syntab)
                ;; Go through all articles.
                (while (setq d (pop data))
-                 (when (setq score
-                             (cdr (assq 
-                                   (gnus-data-mark d)
-                                   gnus-default-adaptive-word-score-alist)))
+                 (when (and
+                        (not (gnus-data-pseudo-p d))
+                        (setq score
+                              (cdr (assq 
+                                    (gnus-data-mark d)
+                                    gnus-default-adaptive-word-score-alist))))
                    ;; This article has a mark that should lead to
                    ;; adaptive word rules, so we insert the subject
                    ;; and find all words in that string.
@@ -2105,18 +2106,19 @@ SCORE is the score to add."
   "List words used in scoring."
   (interactive)
   (let ((alists (gnus-score-load-files (gnus-all-score-files)))
-       alist rule rules)
+       alist rule rules kill)
     ;; Go through all the score alists for this group
     ;; and find all `w' rules.
     (while (setq alist (pop alists))
-      (when (and (stringp (setq rule (pop alist)))
-                (equal "subject" (downcase (pop rule))))
-       (while rule
-         (when (memq (nth 3 (car rule)) '(w W word Word))
-           (push (cons (or (nth 1 rule) gnus-score-interactive-default-score)
-                       (car rule))
-                 rules))
-         (pop rule))))
+      (while (setq rule (pop alist))
+       (when (and (stringp (car rule))
+                  (equal "subject" (downcase (pop rule))))
+         (while (setq kill (pop rule))
+           (when (memq (nth 3 kill) '(w W word Word))
+             (push (cons (or (nth 1 kill)
+                             gnus-score-interactive-default-score)
+                         (car kill))
+                   rules))))))
     (setq rules (sort rules (lambda (r1 r2)
                              (string-lessp (cdr r1) (cdr r2)))))
     ;; Add up words that have appeared several times.
@@ -2129,12 +2131,13 @@ SCORE is the score to add."
          (pop r))))
     ;; Insert the words.
     (nnheader-set-temp-buffer "*Score Words*")
-    (setq rules (sort rules (lambda (r1 r2) (> (car r1) (car r2)))))
-    (while rules
-      (insert (format "%-5d: %s\n" (caar rules) (cdar rules)))
-      (pop rules))
-    (gnus-add-current-to-buffer-list)
-    (gnus-configure-windows 'score-words)))
+    (if (not (setq rules (sort rules (lambda (r1 r2) (> (car r1) (car r2))))))
+       (gnus-error 3 "No word score rules")
+      (while rules
+       (insert (format "%-5d: %s\n" (caar rules) (cdar rules)))
+       (pop rules))
+      (gnus-add-current-to-buffer-list)
+      (gnus-configure-windows 'score-words))))
 
 (defun gnus-summary-rescore ()
   "Redo the entire scoring process in the current summary."
@@ -2326,7 +2329,7 @@ GROUP using BNews sys file syntax."
                         "[/:" (if trans (char-to-string trans) "") "]")))
            (while (re-search-forward regexp nil t)
              (replace-match "." t t)))
-         ;; Cludge to get rid of "nntp+" problems.
+         ;; Kludge to get rid of "nntp+" problems.
          (goto-char (point-min))
          (and (looking-at "nn[a-z]+\\+")
               (progn
@@ -2360,12 +2363,12 @@ GROUP using BNews sys file syntax."
        (setq sfiles (cdr sfiles)))
       (kill-buffer (current-buffer))
       ;; Slight kludge here - the last score file returned should be
-      ;; the local score file, whether it exists or not. This is so
+      ;; the local score file, whether it exists or not.  This is so
       ;; that any score commands the user enters will go to the right
       ;; file, and not end up in some global score file.
       (let ((localscore (gnus-score-file-name group)))
        (setq ofiles (cons localscore (delete localscore ofiles))))
-      (nreverse ofiles))))
+      (gnus-sort-score-files (nreverse ofiles)))))
 
 (defun gnus-score-find-single (group)
   "Return list containing the score file for GROUP."
@@ -2388,6 +2391,38 @@ This includes the score file for the group and all its parents."
             (setq all (nreverse all)))
      (mapcar 'gnus-score-file-name all))))
 
+(defun gnus-score-file-rank (file)
+  "Return a number that says how specific score FILE is.
+Destroys the current buffer."
+  (when (string-match
+        (concat "^" (regexp-quote
+                     (expand-file-name
+                      (file-name-as-directory gnus-kill-files-directory))))
+        file)
+    (setq file (substring file (match-end 0))))
+  (insert file)
+  (goto-char (point-min))
+  (let ((beg (point))
+       elems)
+    (while (re-search-forward "[./]" nil t)
+      (push (buffer-substring beg (1- (point))) 
+           elems))
+    (erase-buffer)
+    (setq elems (delete "all" elems))
+    (length elems)))
+    
+(defun gnus-sort-score-files (files)
+  "Sort FILES so that the most general files come first."
+  (nnheader-temp-write nil
+    (let ((alist
+          (mapcar
+           (lambda (file)
+             (cons (inline (gnus-score-file-rank file)) file))
+           files)))
+      (mapcar
+       (lambda (f) (cdr f))
+       (sort alist (lambda (f1 f2) (< (car f1) (car f2))))))))
+
 (defun gnus-score-find-alist (group)
   "Return list of score files for GROUP.
 The list is determined from the variable gnus-score-file-alist."
@@ -2407,7 +2442,7 @@ The list is determined from the variable gnus-score-file-alist."
       (while alist
        (and (string-match (caar alist) group)
             ;; progn used just in case ("regexp") has no files
-            ;; and score-files is still nil. -sj
+            ;; and score-files is still nil.  -sj
             ;; this can be construed as a "stop searching here" feature :>
             ;; and used to simplify regexps in the single-alist 
             (progn
@@ -2570,7 +2605,7 @@ If ADAPT, return the home adaptive file instead."
   "Decay non-permanent scores in ALIST."
   (let ((times (- (gnus-time-to-day (current-time)) day))
        kill entry updated score n)
-    (unless (zerop times) ;Done decays today already?
+    (unless (zerop times)              ;Done decays today already?
       (while (setq entry (pop alist))
        (when (stringp (car entry))
          (setq entry (cdr entry))