diff options
| author | Chong Yidong | 2008-12-04 22:49:21 +0000 |
|---|---|---|
| committer | Chong Yidong | 2008-12-04 22:49:21 +0000 |
| commit | bc6cdadc9444314c865a3ebf340c18421273ab89 (patch) | |
| tree | f5ffc6a06e66a9e49ffdc7a84b348a0babbadcc8 | |
| parent | 0efcb0dc64b1b7b85a97eebdb023d8dd8ee913d0 (diff) | |
| download | emacs-bc6cdadc9444314c865a3ebf340c18421273ab89.tar.gz emacs-bc6cdadc9444314c865a3ebf340c18421273ab89.zip | |
Sync with rmailkwd.el.
| -rw-r--r-- | lisp/mail/pmailkwd.el | 347 |
1 files changed, 203 insertions, 144 deletions
diff --git a/lisp/mail/pmailkwd.el b/lisp/mail/pmailkwd.el index 6bce28a59e2..4f95cb8f3f3 100644 --- a/lisp/mail/pmailkwd.el +++ b/lisp/mail/pmailkwd.el | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | ;;; pmailkwd.el --- part of the "PMAIL" mail reader for Emacs | 1 | ;;; pmailkwd.el --- part of the "PMAIL" mail reader for Emacs |
| 2 | 2 | ||
| 3 | ;; Copyright (C) 1985, 1988, 1994, 2001, 2002, 2003, 2004, 2005, 2006, | 3 | ;; Copyright (C) 1985, 1988, 1994, 2001, 2002, 2003, 2004, |
| 4 | ;; 2007, 2008 Free Software Foundation, Inc. | 4 | ;; 2005, 2006, 2007, 2008 Free Software Foundation, Inc. |
| 5 | 5 | ||
| 6 | ;; Maintainer: FSF | 6 | ;; Maintainer: FSF |
| 7 | ;; Keywords: mail | 7 | ;; Keywords: mail |
| @@ -23,9 +23,6 @@ | |||
| 23 | 23 | ||
| 24 | ;;; Commentary: | 24 | ;;; Commentary: |
| 25 | 25 | ||
| 26 | ;; This library manages keywords (labels). Labels are stored in the | ||
| 27 | ;; variable `pmail-keywords'. | ||
| 28 | |||
| 29 | ;;; Code: | 26 | ;;; Code: |
| 30 | 27 | ||
| 31 | (defvar pmail-buffer) | 28 | (defvar pmail-buffer) |
| @@ -39,148 +36,158 @@ | |||
| 39 | ;; completion. It is better to use strings with the label functions | 36 | ;; completion. It is better to use strings with the label functions |
| 40 | ;; and let them worry about making the label. | 37 | ;; and let them worry about making the label. |
| 41 | 38 | ||
| 42 | (eval-when-compile | 39 | (defvar pmail-label-obarray (make-vector 47 0)) |
| 43 | (require 'mail-utils)) | ||
| 44 | 40 | ||
| 45 | ;; Named list of symbols representing valid message attributes in PMAIL. | 41 | ;; Named list of symbols representing valid message attributes in PMAIL. |
| 46 | 42 | ||
| 47 | (defconst pmail-attributes | 43 | (defconst pmail-attributes |
| 48 | '(deleted answered filed forwarded unseen edited resent) | 44 | (cons 'pmail-keywords |
| 49 | "Keywords with defined semantics used to label messages. | 45 | (mapcar (function (lambda (s) (intern s pmail-label-obarray))) |
| 50 | These have a well-defined meaning to the PMAIL system.") | 46 | '("deleted" "answered" "filed" "forwarded" "unseen" "edited" |
| 47 | "resent")))) | ||
| 51 | 48 | ||
| 52 | (defconst pmail-deleted-label 'deleted) | 49 | (defconst pmail-deleted-label (intern "deleted" pmail-label-obarray)) |
| 53 | 50 | ||
| 54 | ;; Named list of symbols representing valid message keywords in PMAIL. | 51 | ;; Named list of symbols representing valid message keywords in PMAIL. |
| 55 | 52 | ||
| 56 | (defvar pmail-keywords nil | 53 | (defvar pmail-keywords) |
| 57 | "Keywords used to label messages. | ||
| 58 | These are all user-defined, unlike `pmail-attributes'.") | ||
| 59 | 54 | ||
| 55 | ;;;###autoload | ||
| 56 | (defun pmail-add-label (string) | ||
| 57 | "Add LABEL to labels associated with current PMAIL message. | ||
| 58 | Completion is performed over known labels when reading." | ||
| 59 | (interactive (list (pmail-read-label "Add label"))) | ||
| 60 | (pmail-set-label string t)) | ||
| 60 | 61 | ||
| 61 | ;; External library declarations. | 62 | ;;;###autoload |
| 62 | (declare-function mail-comma-list-regexp "mail-utils" (labels)) | 63 | (defun pmail-kill-label (string) |
| 63 | (declare-function mail-parse-comma-list "mail-utils" ()) | 64 | "Remove LABEL from labels associated with current PMAIL message. |
| 64 | (declare-function pmail-desc-add-keyword "pmaildesc" (keyword n)) | 65 | Completion is performed over known labels when reading." |
| 65 | (declare-function pmail-desc-get-end "pmaildesc" (n)) | 66 | (interactive (list (pmail-read-label "Remove label"))) |
| 66 | (declare-function pmail-desc-get-keywords "pmaildesc" (n)) | 67 | (pmail-set-label string nil)) |
| 67 | (declare-function pmail-desc-get-start "pmaildesc" (n)) | 68 | |
| 68 | (declare-function pmail-desc-remove-keyword "pmaildesc" (keyword n)) | 69 | ;;;###autoload |
| 70 | (defun pmail-read-label (prompt) | ||
| 71 | (with-current-buffer pmail-buffer | ||
| 72 | (if (not pmail-keywords) (pmail-parse-file-keywords)) | ||
| 73 | (let ((result | ||
| 74 | (completing-read (concat prompt | ||
| 75 | (if pmail-last-label | ||
| 76 | (concat " (default " | ||
| 77 | (symbol-name pmail-last-label) | ||
| 78 | "): ") | ||
| 79 | ": ")) | ||
| 80 | pmail-label-obarray | ||
| 81 | nil | ||
| 82 | nil))) | ||
| 83 | (if (string= result "") | ||
| 84 | pmail-last-label | ||
| 85 | (setq pmail-last-label (pmail-make-label result t)))))) | ||
| 86 | |||
| 87 | (declare-function pmail-maybe-set-message-counters "pmail" ()) | ||
| 69 | (declare-function pmail-display-labels "pmail" ()) | 88 | (declare-function pmail-display-labels "pmail" ()) |
| 70 | (declare-function pmail-message-labels-p "pmail" (msg labels)) | ||
| 71 | (declare-function pmail-msgbeg "pmail" (n)) | 89 | (declare-function pmail-msgbeg "pmail" (n)) |
| 72 | (declare-function pmail-set-attribute "pmail" (attr state &optional msgnum)) | 90 | (declare-function pmail-set-message-deleted-p "pmail" (n state)) |
| 91 | (declare-function pmail-message-labels-p "pmail" (msg labels)) | ||
| 73 | (declare-function pmail-show-message "pmail" (&optional n no-summary)) | 92 | (declare-function pmail-show-message "pmail" (&optional n no-summary)) |
| 74 | (declare-function pmail-summary-exists "pmail" ()) | 93 | (declare-function mail-comma-list-regexp "mail-utils" (labels)) |
| 75 | (declare-function pmail-summary-update "pmailsum" (n)) | 94 | (declare-function mail-parse-comma-list "mail-utils.el" ()) |
| 76 | 95 | ||
| 77 | ;;;; Low-level functions. | 96 | (defun pmail-set-label (l state &optional n) |
| 97 | (with-current-buffer pmail-buffer | ||
| 98 | (pmail-maybe-set-message-counters) | ||
| 99 | (if (not n) (setq n pmail-current-message)) | ||
| 100 | (aset pmail-summary-vector (1- n) nil) | ||
| 101 | (let* ((attribute (pmail-attribute-p l)) | ||
| 102 | (keyword (and (not attribute) | ||
| 103 | (or (pmail-keyword-p l) | ||
| 104 | (pmail-install-keyword l)))) | ||
| 105 | (label (or attribute keyword))) | ||
| 106 | (if label | ||
| 107 | (let ((omax (- (buffer-size) (point-max))) | ||
| 108 | (omin (- (buffer-size) (point-min))) | ||
| 109 | (buffer-read-only nil) | ||
| 110 | (case-fold-search t)) | ||
| 111 | (unwind-protect | ||
| 112 | (save-excursion | ||
| 113 | (widen) | ||
| 114 | (goto-char (pmail-msgbeg n)) | ||
| 115 | (forward-line 1) | ||
| 116 | (if (not (looking-at "[01],")) | ||
| 117 | nil | ||
| 118 | (let ((start (1+ (point))) | ||
| 119 | (bound)) | ||
| 120 | (narrow-to-region (point) (progn (end-of-line) (point))) | ||
| 121 | (setq bound (point-max)) | ||
| 122 | (search-backward ",," nil t) | ||
| 123 | (if attribute | ||
| 124 | (setq bound (1+ (point))) | ||
| 125 | (setq start (1+ (point)))) | ||
| 126 | (goto-char start) | ||
| 127 | ; (while (re-search-forward "[ \t]*,[ \t]*" nil t) | ||
| 128 | ; (replace-match ",")) | ||
| 129 | ; (goto-char start) | ||
| 130 | (if (re-search-forward | ||
| 131 | (concat ", " (pmail-quote-label-name label) ",") | ||
| 132 | bound | ||
| 133 | 'move) | ||
| 134 | (if (not state) (replace-match ",")) | ||
| 135 | (if state (insert " " (symbol-name label) ","))) | ||
| 136 | (if (eq label pmail-deleted-label) | ||
| 137 | (pmail-set-message-deleted-p n state))))) | ||
| 138 | (narrow-to-region (- (buffer-size) omin) (- (buffer-size) omax)) | ||
| 139 | (if (= n pmail-current-message) (pmail-display-labels)))))))) | ||
| 140 | |||
| 141 | ;; Commented functions aren't used by PMAIL but might be nice for user | ||
| 142 | ;; packages that do stuff with PMAIL. Note that pmail-message-labels-p | ||
| 143 | ;; is in pmail.el now. | ||
| 144 | |||
| 145 | ;(defun pmail-message-label-p (label &optional n) | ||
| 146 | ; "Returns symbol if LABEL (attribute or keyword) on NTH or current message." | ||
| 147 | ; (pmail-message-labels-p (or n pmail-current-message) (regexp-quote label))) | ||
| 148 | |||
| 149 | ;(defun pmail-parse-message-labels (&optional n) | ||
| 150 | ; "Returns labels associated with NTH or current PMAIL message. | ||
| 151 | ;The result is a list of two lists of strings. The first is the | ||
| 152 | ;message attributes and the second is the message keywords." | ||
| 153 | ; (let (atts keys) | ||
| 154 | ; (save-restriction | ||
| 155 | ; (widen) | ||
| 156 | ; (goto-char (pmail-msgbeg (or n pmail-current-message))) | ||
| 157 | ; (forward-line 1) | ||
| 158 | ; (or (looking-at "[01],") (error "Malformed label line")) | ||
| 159 | ; (forward-char 2) | ||
| 160 | ; (while (looking-at "[ \t]*\\([^ \t\n,]+\\),") | ||
| 161 | ; (setq atts (cons (buffer-substring (match-beginning 1) (match-end 1)) | ||
| 162 | ; atts)) | ||
| 163 | ; (goto-char (match-end 0))) | ||
| 164 | ; (or (looking-at ",") (error "Malformed label line")) | ||
| 165 | ; (forward-char 1) | ||
| 166 | ; (while (looking-at "[ \t]*\\([^ \t\n,]+\\),") | ||
| 167 | ; (setq keys (cons (buffer-substring (match-beginning 1) (match-end 1)) | ||
| 168 | ; keys)) | ||
| 169 | ; (goto-char (match-end 0))) | ||
| 170 | ; (or (looking-at "[ \t]*$") (error "Malformed label line")) | ||
| 171 | ; (list (nreverse atts) (nreverse keys))))) | ||
| 78 | 172 | ||
| 79 | (defun pmail-attribute-p (s) | 173 | (defun pmail-attribute-p (s) |
| 80 | "Non-nil if S is a known attribute. | ||
| 81 | See `pmail-attributes'." | ||
| 82 | (let ((symbol (pmail-make-label s))) | 174 | (let ((symbol (pmail-make-label s))) |
| 83 | (memq symbol pmail-attributes))) | 175 | (if (memq symbol (cdr pmail-attributes)) symbol))) |
| 84 | 176 | ||
| 85 | (defun pmail-keyword-p (s) | 177 | (defun pmail-keyword-p (s) |
| 86 | "Non-nil if S is a known keyword for this Pmail file. | ||
| 87 | See `pmail-keywords'." | ||
| 88 | (let ((symbol (pmail-make-label s))) | 178 | (let ((symbol (pmail-make-label s))) |
| 89 | (memq symbol pmail-keywords))) | 179 | (if (memq symbol (cdr (pmail-keywords))) symbol))) |
| 90 | 180 | ||
| 91 | (defun pmail-make-label (s &optional forcep) | 181 | (defun pmail-make-label (s &optional forcep) |
| 92 | (cond ((symbolp s) s) | 182 | (cond ((symbolp s) s) |
| 93 | (forcep (intern (downcase s))) | 183 | (forcep (intern (downcase s) pmail-label-obarray)) |
| 94 | (t (intern-soft (downcase s))))) | 184 | (t (intern-soft (downcase s) pmail-label-obarray)))) |
| 185 | |||
| 186 | (defun pmail-force-make-label (s) | ||
| 187 | (intern (downcase s) pmail-label-obarray)) | ||
| 95 | 188 | ||
| 96 | (defun pmail-quote-label-name (label) | 189 | (defun pmail-quote-label-name (label) |
| 97 | (regexp-quote (symbol-name (pmail-make-label label t)))) | 190 | (regexp-quote (symbol-name (pmail-make-label label t)))) |
| 98 | |||
| 99 | ;;;###autoload | ||
| 100 | (defun pmail-register-keywords (words) | ||
| 101 | "Add the strings in WORDS to `pmail-keywords'." | ||
| 102 | (dolist (word words) | ||
| 103 | (pmail-register-keyword word))) | ||
| 104 | |||
| 105 | (defun pmail-register-keyword (word) | ||
| 106 | "Append the string WORD to `pmail-keywords', | ||
| 107 | unless it already is a keyword or an attribute." | ||
| 108 | (let ((keyword (pmail-make-label word t))) | ||
| 109 | (unless (or (pmail-attribute-p keyword) | ||
| 110 | (pmail-keyword-p keyword)) | ||
| 111 | (setq pmail-keywords (cons keyword pmail-keywords))))) | ||
| 112 | |||
| 113 | ;;;; Adding and removing message keywords. | ||
| 114 | |||
| 115 | ;;;###autoload | ||
| 116 | (defun pmail-add-label (string) | ||
| 117 | "Add LABEL to labels associated with current PMAIL message." | ||
| 118 | (interactive (list (pmail-read-label "Add label"))) | ||
| 119 | (pmail-set-label (pmail-make-label string) t) | ||
| 120 | (pmail-display-labels)) | ||
| 121 | |||
| 122 | ;;;###autoload | ||
| 123 | (defun pmail-kill-label (string) | ||
| 124 | "Remove LABEL from labels associated with current PMAIL message." | ||
| 125 | (interactive (list (pmail-read-label "Remove label" t))) | ||
| 126 | (pmail-set-label (pmail-make-label string) nil)) | ||
| 127 | |||
| 128 | ;;;###autoload | ||
| 129 | (defun pmail-read-label (prompt &optional existing) | ||
| 130 | "Ask for a label using PROMPT. | ||
| 131 | If EXISTING is non-nil, ask for one of the labels of the current | ||
| 132 | message." | ||
| 133 | (when (= pmail-total-messages 0) | ||
| 134 | (error "No messages in this file")) | ||
| 135 | (with-current-buffer pmail-buffer | ||
| 136 | (let ((result (if existing | ||
| 137 | (let* ((keywords (pmail-desc-get-keywords | ||
| 138 | pmail-current-message)) | ||
| 139 | (last (symbol-name pmail-last-label)) | ||
| 140 | (default (if (member last keywords) | ||
| 141 | last | ||
| 142 | (car keywords)))) | ||
| 143 | (unless keywords | ||
| 144 | (error "No labels for the current message")) | ||
| 145 | (completing-read | ||
| 146 | (concat prompt " (default " default "): ") | ||
| 147 | keywords nil t nil nil default)) | ||
| 148 | (let ((default (symbol-name pmail-last-label))) | ||
| 149 | (completing-read | ||
| 150 | (concat prompt (if pmail-last-label | ||
| 151 | (concat " (default " default "): ") | ||
| 152 | ": ")) | ||
| 153 | (mapcar 'list pmail-keywords) | ||
| 154 | nil nil nil nil default))))) | ||
| 155 | (setq pmail-last-label (pmail-make-label result t)) | ||
| 156 | ;; return the string, not the symbol | ||
| 157 | result))) | ||
| 158 | |||
| 159 | (defun pmail-set-label (l state &optional n) | ||
| 160 | "Add or remove label L in message N. | ||
| 161 | The label L is added when STATE is non-nil, otherwise it is | ||
| 162 | removed. If N is nil then use the current Pmail message. The | ||
| 163 | current buffer, possibly narrowed, displays a message." | ||
| 164 | (if (= pmail-total-messages 0) | ||
| 165 | (error "No messages in this file")) | ||
| 166 | (with-current-buffer pmail-buffer | ||
| 167 | (if (not n) (setq n pmail-current-message)) | ||
| 168 | (save-restriction | ||
| 169 | (widen) | ||
| 170 | (narrow-to-region (pmail-desc-get-start n) (pmail-desc-get-end n)) | ||
| 171 | ;; FIXME: we should move all string-using functions to symbols! | ||
| 172 | (let ((str (symbol-name l))) | ||
| 173 | (if (pmail-attribute-p l) | ||
| 174 | (pmail-set-attribute str state n) | ||
| 175 | ;; Make sure the keyword is registered. | ||
| 176 | (pmail-register-keyword l) | ||
| 177 | (if state | ||
| 178 | (pmail-desc-add-keyword str n) | ||
| 179 | (pmail-desc-remove-keyword str n)))))) | ||
| 180 | (pmail-display-labels) | ||
| 181 | ;; Deal with the summary buffer. | ||
| 182 | (when (pmail-summary-exists) | ||
| 183 | (pmail-summary-update n))) | ||
| 184 | 191 | ||
| 185 | ;; Motion on messages with keywords. | 192 | ;; Motion on messages with keywords. |
| 186 | 193 | ||
| @@ -200,32 +207,84 @@ LABELS should be a comma-separated list of label names. | |||
| 200 | If LABELS is empty, the last set of labels specified is used. | 207 | If LABELS is empty, the last set of labels specified is used. |
| 201 | With prefix argument N moves forward N messages with these labels." | 208 | With prefix argument N moves forward N messages with these labels." |
| 202 | (interactive "p\nsMove to next msg with labels: ") | 209 | (interactive "p\nsMove to next msg with labels: ") |
| 203 | (when (string= labels "") | 210 | (if (string= labels "") |
| 204 | (setq labels pmail-last-multi-labels)) | 211 | (setq labels pmail-last-multi-labels)) |
| 205 | (unless labels | 212 | (or labels |
| 206 | (error "No labels to find have been specified previously")) | 213 | (error "No labels to find have been specified previously")) |
| 207 | (with-current-buffer pmail-buffer | 214 | (set-buffer pmail-buffer) |
| 208 | (setq pmail-last-multi-labels labels) | 215 | (setq pmail-last-multi-labels labels) |
| 209 | (let ((lastwin pmail-current-message) | 216 | (pmail-maybe-set-message-counters) |
| 210 | (current pmail-current-message) | 217 | (let ((lastwin pmail-current-message) |
| 211 | (regexp (concat ", ?\\(" | 218 | (current pmail-current-message) |
| 212 | (mail-comma-list-regexp labels) | 219 | (regexp (concat ", ?\\(" |
| 213 | "\\),"))) | 220 | (mail-comma-list-regexp labels) |
| 214 | (save-restriction | 221 | "\\),"))) |
| 215 | (widen) | 222 | (save-restriction |
| 216 | (while (and (> n 0) (< current pmail-total-messages)) | 223 | (widen) |
| 217 | (setq current (1+ current)) | 224 | (while (and (> n 0) (< current pmail-total-messages)) |
| 218 | (when (pmail-message-labels-p current regexp) | 225 | (setq current (1+ current)) |
| 226 | (if (pmail-message-labels-p current regexp) | ||
| 219 | (setq lastwin current n (1- n)))) | 227 | (setq lastwin current n (1- n)))) |
| 220 | (while (and (< n 0) (> current 1)) | 228 | (while (and (< n 0) (> current 1)) |
| 221 | (setq current (1- current)) | 229 | (setq current (1- current)) |
| 222 | (when (pmail-message-labels-p current regexp) | 230 | (if (pmail-message-labels-p current regexp) |
| 223 | (setq lastwin current n (1+ n))))) | 231 | (setq lastwin current n (1+ n))))) |
| 224 | (pmail-show-message lastwin) | 232 | (pmail-show-message lastwin) |
| 225 | (when (< n 0) | 233 | (if (< n 0) |
| 226 | (message "No previous message with labels %s" labels)) | 234 | (message "No previous message with labels %s" labels)) |
| 227 | (when (> n 0) | 235 | (if (> n 0) |
| 228 | (message "No following message with labels %s" labels))))) | 236 | (message "No following message with labels %s" labels)))) |
| 237 | |||
| 238 | ;;; Manipulate the file's Labels option. | ||
| 239 | |||
| 240 | ;; Return a list of symbols for all | ||
| 241 | ;; the keywords (labels) recorded in this file's Labels option. | ||
| 242 | (defun pmail-keywords () | ||
| 243 | (or pmail-keywords (pmail-parse-file-keywords))) | ||
| 244 | |||
| 245 | ;; Set pmail-keywords to a list of symbols for all | ||
| 246 | ;; the keywords (labels) recorded in this file's Labels option. | ||
| 247 | (defun pmail-parse-file-keywords () | ||
| 248 | (save-restriction | ||
| 249 | (save-excursion | ||
| 250 | (widen) | ||
| 251 | (goto-char 1) | ||
| 252 | (setq pmail-keywords | ||
| 253 | (if (search-forward "\nLabels:" (pmail-msgbeg 1) t) | ||
| 254 | (progn | ||
| 255 | (narrow-to-region (point) (progn (end-of-line) (point))) | ||
| 256 | (goto-char (point-min)) | ||
| 257 | (cons 'pmail-keywords | ||
| 258 | (mapcar 'pmail-force-make-label | ||
| 259 | (mail-parse-comma-list))))))))) | ||
| 260 | |||
| 261 | ;; Add WORD to the list in the file's Labels option. | ||
| 262 | ;; Any keyword used for the first time needs this done. | ||
| 263 | (defun pmail-install-keyword (word) | ||
| 264 | (let ((keyword (pmail-make-label word t)) | ||
| 265 | (keywords (pmail-keywords))) | ||
| 266 | (if (not (or (pmail-attribute-p keyword) | ||
| 267 | (pmail-keyword-p keyword))) | ||
| 268 | (let ((omin (- (buffer-size) (point-min))) | ||
| 269 | (omax (- (buffer-size) (point-max)))) | ||
| 270 | (unwind-protect | ||
| 271 | (save-excursion | ||
| 272 | (widen) | ||
| 273 | (goto-char 1) | ||
| 274 | (let ((case-fold-search t) | ||
| 275 | (buffer-read-only nil)) | ||
| 276 | (or (search-forward "\nLabels:" nil t) | ||
| 277 | (progn | ||
| 278 | (end-of-line) | ||
| 279 | (insert "\nLabels:"))) | ||
| 280 | (delete-region (point) (progn (end-of-line) (point))) | ||
| 281 | (setcdr keywords (cons keyword (cdr keywords))) | ||
| 282 | (while (setq keywords (cdr keywords)) | ||
| 283 | (insert (symbol-name (car keywords)) ",")) | ||
| 284 | (delete-char -1))) | ||
| 285 | (narrow-to-region (- (buffer-size) omin) | ||
| 286 | (- (buffer-size) omax))))) | ||
| 287 | keyword)) | ||
| 229 | 288 | ||
| 230 | (provide 'pmailkwd) | 289 | (provide 'pmailkwd) |
| 231 | 290 | ||