comparison lisp/mh-e/mh-seq.el @ 67758:6b063593fdad

Follow Emacs coding conventions. Use default setting of emacs-lisp-docstring-fill-column which is 65.
author Bill Wohler <wohler@newt.com>
date Fri, 23 Dec 2005 07:40:40 +0000
parents 3a8785724cca
children 9c3504ae6060
comparison
equal deleted inserted replaced
67757:488b4dbc7482 67758:6b063593fdad
120 "Table to look up message identifier from message index.") 120 "Table to look up message identifier from message index.")
121 (defvar mh-thread-scan-line-map nil 121 (defvar mh-thread-scan-line-map nil
122 "Map of message index to various parts of the scan line.") 122 "Map of message index to various parts of the scan line.")
123 (defvar mh-thread-scan-line-map-stack nil 123 (defvar mh-thread-scan-line-map-stack nil
124 "Old map of message index to various parts of the scan line. 124 "Old map of message index to various parts of the scan line.
125 This is the original map that is stored when the folder is narrowed.") 125 This is the original map that is stored when the folder is
126 narrowed.")
126 (defvar mh-thread-subject-container-hash nil 127 (defvar mh-thread-subject-container-hash nil
127 "Hashtable used to group messages by subject.") 128 "Hashtable used to group messages by subject.")
128 (defvar mh-thread-duplicates nil 129 (defvar mh-thread-duplicates nil
129 "Hashtable used to associate messages with the same message identifier.") 130 "Hashtable used to associate messages with the same message identifier.")
130 (defvar mh-thread-history () 131 (defvar mh-thread-history ()
131 "Variable to remember the transformations to the thread tree. 132 "Variable to remember the transformations to the thread tree.
132 When new messages are added, these transformations are rewound, then the 133 When new messages are added, these transformations are rewound,
133 links are added from the newly seen messages. Finally the transformations are 134 then the links are added from the newly seen messages. Finally
134 redone to get the new thread tree. This makes incremental threading easier.") 135 the transformations are redone to get the new thread tree. This
136 makes incremental threading easier.")
135 (defvar mh-thread-body-width nil 137 (defvar mh-thread-body-width nil
136 "Width of scan substring that contains subject and body of message.") 138 "Width of scan substring that contains subject and body of message.")
137 139
138 (make-variable-buffer-local 'mh-thread-id-hash) 140 (make-variable-buffer-local 'mh-thread-id-hash)
139 (make-variable-buffer-local 'mh-thread-subject-hash) 141 (make-variable-buffer-local 'mh-thread-subject-hash)
148 150
149 ;;;###mh-autoload 151 ;;;###mh-autoload
150 (defun mh-delete-seq (sequence) 152 (defun mh-delete-seq (sequence)
151 "Delete SEQUENCE. 153 "Delete SEQUENCE.
152 154
153 You are prompted for the sequence to delete. Note that this deletes only the 155 You are prompted for the sequence to delete. Note that this
154 sequence, not the messages in the sequence. If you want to delete the 156 deletes only the sequence, not the messages in the sequence. If
155 messages, use \"\\[universal-argument] \\[mh-delete-msg]\"." 157 you want to delete the messages, use \"\\[universal-argument]
158 \\[mh-delete-msg]\"."
156 (interactive (list (mh-read-seq-default "Delete" t))) 159 (interactive (list (mh-read-seq-default "Delete" t)))
157 (let ((msg-list (mh-seq-to-msgs sequence)) 160 (let ((msg-list (mh-seq-to-msgs sequence))
158 (internal-flag (mh-internal-seq sequence)) 161 (internal-flag (mh-internal-seq sequence))
159 (folders-changed (list mh-current-folder))) 162 (folders-changed (list mh-current-folder)))
160 (mh-iterate-on-range msg sequence 163 (mh-iterate-on-range msg sequence
214 (message "Listing sequences...done"))))) 217 (message "Listing sequences...done")))))
215 218
216 ;;;###mh-autoload 219 ;;;###mh-autoload
217 (defun mh-msg-is-in-seq (message) 220 (defun mh-msg-is-in-seq (message)
218 "Display the sequences in which the current message appears. 221 "Display the sequences in which the current message appears.
219 Use a prefix argument to display the sequences in which another MESSAGE 222 Use a prefix argument to display the sequences in which another
220 appears." 223 MESSAGE appears."
221 (interactive "P") 224 (interactive "P")
222 (if (not message) 225 (if (not message)
223 (setq message (mh-get-msg-num t))) 226 (setq message (mh-get-msg-num t)))
224 (let* ((dest-folder (loop for seq in mh-refile-list 227 (let* ((dest-folder (loop for seq in mh-refile-list
225 when (member message (cdr seq)) return (car seq) 228 when (member message (cdr seq)) return (car seq)
241 244
242 ;;;###mh-autoload 245 ;;;###mh-autoload
243 (defun mh-narrow-to-seq (sequence) 246 (defun mh-narrow-to-seq (sequence)
244 "Restrict display to messages in SEQUENCE. 247 "Restrict display to messages in SEQUENCE.
245 248
246 You are prompted for the name of the sequence. What this command does is show 249 You are prompted for the name of the sequence. What this command
247 only those messages that are in the selected sequence in the MH-Folder buffer. 250 does is show only those messages that are in the selected
248 In addition, it limits further MH-E searches to just those messages. 251 sequence in the MH-Folder buffer. In addition, it limits further
249 252 MH-E searches to just those messages.
250 When you want to widen the view to all your messages again, use \\[mh-widen]." 253
254 When you want to widen the view to all your messages again, use
255 \\[mh-widen]."
251 (interactive (list (mh-read-seq "Narrow to" t))) 256 (interactive (list (mh-read-seq "Narrow to" t)))
252 (with-mh-folder-updating (t) 257 (with-mh-folder-updating (t)
253 (cond ((mh-seq-to-msgs sequence) 258 (cond ((mh-seq-to-msgs sequence)
254 (mh-remove-all-notation) 259 (mh-remove-all-notation)
255 (let ((eob (point-max)) 260 (let ((eob (point-max))
281 286
282 ;;;###mh-autoload 287 ;;;###mh-autoload
283 (defun mh-put-msg-in-seq (range sequence) 288 (defun mh-put-msg-in-seq (range sequence)
284 "Add RANGE to SEQUENCE\\<mh-folder-mode-map>. 289 "Add RANGE to SEQUENCE\\<mh-folder-mode-map>.
285 290
286 To place a message in a sequence, use this command to do it manually, or use 291 To place a message in a sequence, use this command to do it
287 the MH command \"pick\" or the MH-E version of \"pick\", \\[mh-search-folder], 292 manually, or use the MH command \"pick\" or the MH-E version of
288 which create a sequence automatically. 293 \"pick\", \\[mh-search-folder], which create a sequence
289 294 automatically.
290 Give this command a RANGE and you can add all the messages in a sequence to 295
291 another sequence (for example, \"\\[universal-argument] \\[mh-put-msg-in-seq] 296 Give this command a RANGE and you can add all the messages in a
292 SourceSequence RET DestSequence RET\"). Check the documentation of 297 sequence to another sequence (for example,
293 `mh-interactive-range' to see how RANGE is read in interactive use." 298 \"\\[universal-argument] \\[mh-put-msg-in-seq] SourceSequence RET
299 DestSequence RET\"). Check the documentation of
300 `mh-interactive-range' to see how RANGE is read in interactive
301 use."
294 (interactive (list (mh-interactive-range "Add messages from") 302 (interactive (list (mh-interactive-range "Add messages from")
295 (mh-read-seq-default "Add to" nil))) 303 (mh-read-seq-default "Add to" nil)))
296 (unless (mh-valid-seq-p sequence) 304 (unless (mh-valid-seq-p sequence)
297 (error "Can't put message in invalid sequence `%s'" sequence)) 305 (error "Can't put message in invalid sequence `%s'" sequence))
298 (let* ((internal-seq-flag (mh-internal-seq sequence)) 306 (let* ((internal-seq-flag (mh-internal-seq sequence))
319 (t nil))) 327 (t nil)))
320 328
321 ;;;###mh-autoload 329 ;;;###mh-autoload
322 (defun mh-widen (&optional all-flag) 330 (defun mh-widen (&optional all-flag)
323 "Remove last restriction. 331 "Remove last restriction.
324 If optional prefix argument ALL-FLAG is non-nil, remove all limits." 332 If optional prefix argument ALL-FLAG is non-nil, remove all
333 limits."
325 (interactive "P") 334 (interactive "P")
326 (let ((msg (mh-get-msg-num nil))) 335 (let ((msg (mh-get-msg-num nil)))
327 (when mh-folder-view-stack 336 (when mh-folder-view-stack
328 (cond (all-flag 337 (cond (all-flag
329 (while (cdr mh-view-ops) 338 (while (cdr mh-view-ops)
366 ;; FIXME? We may want to clear all notations and add one for current-message 375 ;; FIXME? We may want to clear all notations and add one for current-message
367 ;; and process user sequences. 376 ;; and process user sequences.
368 ;;;###mh-autoload 377 ;;;###mh-autoload
369 (defun mh-notate-deleted-and-refiled () 378 (defun mh-notate-deleted-and-refiled ()
370 "Notate messages marked for deletion or refiling. 379 "Notate messages marked for deletion or refiling.
371 Messages to be deleted are given by `mh-delete-list' while messages to be 380 Messages to be deleted are given by `mh-delete-list' while
372 refiled are present in `mh-refile-list'." 381 messages to be refiled are present in `mh-refile-list'."
373 (let ((refiled-hash (make-hash-table)) 382 (let ((refiled-hash (make-hash-table))
374 (deleted-hash (make-hash-table))) 383 (deleted-hash (make-hash-table)))
375 (dolist (msg mh-delete-list) 384 (dolist (msg mh-delete-list)
376 (setf (gethash msg deleted-hash) t)) 385 (setf (gethash msg deleted-hash) t))
377 (dolist (dest-msg-list mh-refile-list) 386 (dolist (dest-msg-list mh-refile-list)
393 (defvar mh-sequence-history ()) 402 (defvar mh-sequence-history ())
394 403
395 ;;;###mh-autoload 404 ;;;###mh-autoload
396 (defun mh-read-seq-default (prompt not-empty) 405 (defun mh-read-seq-default (prompt not-empty)
397 "Read and return sequence name with default narrowed or previous sequence. 406 "Read and return sequence name with default narrowed or previous sequence.
398 PROMPT is the prompt to use when reading. If NOT-EMPTY is non-nil then a 407 PROMPT is the prompt to use when reading. If NOT-EMPTY is non-nil
399 non-empty sequence is read." 408 then a non-empty sequence is read."
400 (mh-read-seq prompt not-empty 409 (mh-read-seq prompt not-empty
401 (or mh-last-seq-used 410 (or mh-last-seq-used
402 (car (mh-seq-containing-msg (mh-get-msg-num nil) nil))))) 411 (car (mh-seq-containing-msg (mh-get-msg-num nil) nil)))))
403 412
404 (defun mh-read-seq (prompt not-empty &optional default) 413 (defun mh-read-seq (prompt not-empty &optional default)
405 "Read and return a sequence name. 414 "Read and return a sequence name.
406 Prompt with PROMPT, raise an error if the sequence is empty and the NOT-EMPTY 415 Prompt with PROMPT, raise an error if the sequence is empty and
407 flag is non-nil, and supply an optional DEFAULT sequence. A reply of '%' 416 the NOT-EMPTY flag is non-nil, and supply an optional DEFAULT
408 defaults to the first sequence containing the current message." 417 sequence. A reply of '%' defaults to the first sequence
418 containing the current message."
409 (let* ((input (completing-read (format "%s %s %s" prompt "sequence:" 419 (let* ((input (completing-read (format "%s %s %s" prompt "sequence:"
410 (if default 420 (if default
411 (format "[%s] " default) 421 (format "[%s] " default)
412 "")) 422 ""))
413 (mh-seq-names mh-seq-list) 423 (mh-seq-names mh-seq-list)
457 ;;;###mh-autoload 467 ;;;###mh-autoload
458 (defun mh-read-range (prompt &optional folder default 468 (defun mh-read-range (prompt &optional folder default
459 expand-flag ask-flag number-as-range-flag) 469 expand-flag ask-flag number-as-range-flag)
460 "Read a message range with PROMPT. 470 "Read a message range with PROMPT.
461 471
462 If FOLDER is non-nil then a range is read from that folder, otherwise use 472 If FOLDER is non-nil then a range is read from that folder, otherwise
463 `mh-current-folder'. 473 use `mh-current-folder'.
464 474
465 If DEFAULT is a string then use that as default range to return. If DEFAULT is 475 If DEFAULT is a string then use that as default range to return. If
466 nil then ask user with default answer a range based on the sequences that seem 476 DEFAULT is nil then ask user with default answer a range based on the
467 relevant. Finally if DEFAULT is t, try to avoid prompting the user. Unseen 477 sequences that seem relevant. Finally if DEFAULT is t, try to avoid
468 messages, if present, are returned. If the folder has fewer than 478 prompting the user. Unseen messages, if present, are returned. If the
469 `mh-large-folder' messages then \"all\" messages are returned. Finally as a 479 folder has fewer than `mh-large-folder' messages then \"all\" messages
470 last resort prompt the user. 480 are returned. Finally as a last resort prompt the user.
471 481
472 If EXPAND-FLAG is non-nil then a list of message numbers corresponding to the 482 If EXPAND-FLAG is non-nil then a list of message numbers corresponding
473 input is returned. If this list is empty then an error is raised. If 483 to the input is returned. If this list is empty then an error is
474 EXPAND-FLAG is nil just return the input string. In this case we don't check 484 raised. If EXPAND-FLAG is nil just return the input string. In this
475 if the range is empty. 485 case we don't check if the range is empty.
476 486
477 If ASK-FLAG is non-nil, then the user is always queried for a range of 487 If ASK-FLAG is non-nil, then the user is always queried for a range of
478 messages. If ASK-FLAG is nil, then the function checks if the unseen sequence 488 messages. If ASK-FLAG is nil, then the function checks if the unseen
479 is non-empty. If that is the case, `mh-unseen-seq', or the list of messages in 489 sequence is non-empty. If that is the case, `mh-unseen-seq', or the
480 it depending on the value of EXPAND, is returned. Otherwise if the folder has 490 list of messages in it depending on the value of EXPAND, is returned.
481 fewer than `mh-large-folder' messages then the list of messages corresponding 491 Otherwise if the folder has fewer than `mh-large-folder' messages then
482 to \"all\" is returned. If neither of the above holds then as a last resort 492 the list of messages corresponding to \"all\" is returned. If neither
483 the user is queried for a range of messages. 493 of the above holds then as a last resort the user is queried for a
484 494 range of messages.
485 If NUMBER-AS-RANGE-FLAG is non-nil, then if a number, N is read as input, it 495
486 is interpreted as the range \"last:N\". 496 If NUMBER-AS-RANGE-FLAG is non-nil, then if a number, N is read as
487 497 input, it is interpreted as the range \"last:N\".
488 This function replaces the existing function `mh-read-msg-range'. Calls to: 498
499 This function replaces the existing function `mh-read-msg-range'.
500 Calls to:
501
489 (mh-read-msg-range folder flag) 502 (mh-read-msg-range folder flag)
503
490 should be replaced with: 504 should be replaced with:
505
491 (mh-read-range \"Suitable prompt\" folder t nil flag 506 (mh-read-range \"Suitable prompt\" folder t nil flag
492 mh-interpret-number-as-range-flag)" 507 mh-interpret-number-as-range-flag)"
493 (setq default (or default mh-last-seq-used 508 (setq default (or default mh-last-seq-used
494 (car (mh-seq-containing-msg (mh-get-msg-num nil) t))) 509 (car (mh-seq-containing-msg (mh-get-msg-num nil) t)))
495 prompt (format "%s range" prompt)) 510 prompt (format "%s range" prompt))
563 (rplaca old-seq new-name))) 578 (rplaca old-seq new-name)))
564 579
565 ;;;###mh-autoload 580 ;;;###mh-autoload
566 (defun mh-notate-cur () 581 (defun mh-notate-cur ()
567 "Mark the MH sequence cur. 582 "Mark the MH sequence cur.
568 In addition to notating the current message with `mh-note-cur' the function 583 In addition to notating the current message with `mh-note-cur'
569 uses `overlay-arrow-position' to put a marker in the fringe." 584 the function uses `overlay-arrow-position' to put a marker in the
585 fringe."
570 (let ((cur (car (mh-seq-to-msgs 'cur)))) 586 (let ((cur (car (mh-seq-to-msgs 'cur))))
571 (when (and cur (mh-goto-msg cur t t)) 587 (when (and cur (mh-goto-msg cur t t))
572 (beginning-of-line) 588 (beginning-of-line)
573 (when (looking-at mh-scan-good-msg-regexp) 589 (when (looking-at mh-scan-good-msg-regexp)
574 (mh-notate nil mh-note-cur mh-cmd-note)) 590 (mh-notate nil mh-note-cur mh-cmd-note))
615 (mh-index-insert-folder-headers))))))) 631 (mh-index-insert-folder-headers)))))))
616 632
617 ;;;###mh-autoload 633 ;;;###mh-autoload
618 (defmacro mh-iterate-on-messages-in-region (var begin end &rest body) 634 (defmacro mh-iterate-on-messages-in-region (var begin end &rest body)
619 "Iterate over region. 635 "Iterate over region.
620 VAR is bound to the message on the current line as we loop starting from BEGIN 636
621 till END. In each step BODY is executed. 637 VAR is bound to the message on the current line as we loop
638 starting from BEGIN till END. In each step BODY is executed.
622 639
623 If VAR is nil then the loop is executed without any binding." 640 If VAR is nil then the loop is executed without any binding."
624 (unless (symbolp var) 641 (unless (symbolp var)
625 (error "Can not bind the non-symbol %s" var)) 642 (error "Can not bind the non-symbol %s" var))
626 (let ((binding-needed-flag var)) 643 (let ((binding-needed-flag var))
637 654
638 ;;;###mh-autoload 655 ;;;###mh-autoload
639 (defmacro mh-iterate-on-range (var range &rest body) 656 (defmacro mh-iterate-on-range (var range &rest body)
640 "Iterate an operation over a region or sequence. 657 "Iterate an operation over a region or sequence.
641 658
642 VAR is bound to each message in turn in a loop over RANGE, which can be a 659 VAR is bound to each message in turn in a loop over RANGE, which
643 message number, a list of message numbers, a sequence, a region in a cons 660 can be a message number, a list of message numbers, a sequence, a
644 cell, or a MH range (something like last:20) in a string. In each iteration, 661 region in a cons cell, or a MH range (something like last:20) in
645 BODY is executed. 662 a string. In each iteration, BODY is executed.
646 663
647 The parameter RANGE is usually created with `mh-interactive-range' 664 The parameter RANGE is usually created with
648 in order to provide a uniform interface to MH-E functions." 665 `mh-interactive-range' in order to provide a uniform interface to
666 MH-E functions."
649 (unless (symbolp var) 667 (unless (symbolp var)
650 (error "Can not bind the non-symbol %s" var)) 668 (error "Can not bind the non-symbol %s" var))
651 (let ((binding-needed-flag var) 669 (let ((binding-needed-flag var)
652 (msgs (make-symbol "msgs")) 670 (msgs (make-symbol "msgs"))
653 (seq-hash-table (make-symbol "seq-hash-table"))) 671 (seq-hash-table (make-symbol "seq-hash-table")))
678 696
679 ;;;###mh-autoload 697 ;;;###mh-autoload
680 (defun mh-range-to-msg-list (range) 698 (defun mh-range-to-msg-list (range)
681 "Return a list of messages for RANGE. 699 "Return a list of messages for RANGE.
682 700
683 Check the documentation of `mh-interactive-range' to see how RANGE is read in 701 Check the documentation of `mh-interactive-range' to see how
684 interactive use." 702 RANGE is read in interactive use."
685 (let (msg-list) 703 (let (msg-list)
686 (mh-iterate-on-range msg range 704 (mh-iterate-on-range msg range
687 (push msg msg-list)) 705 (push msg msg-list))
688 (nreverse msg-list))) 706 (nreverse msg-list)))
689 707
690 ;;;###mh-autoload 708 ;;;###mh-autoload
691 (defun mh-interactive-range (range-prompt &optional default) 709 (defun mh-interactive-range (range-prompt &optional default)
692 "Return interactive specification for message, sequence, range or region. 710 "Return interactive specification for message, sequence, range or region.
693 By convention, the name of this argument is RANGE. 711 By convention, the name of this argument is RANGE.
694 712
695 If variable `transient-mark-mode' is non-nil and the mark is active, then this 713 If variable `transient-mark-mode' is non-nil and the mark is active,
696 function returns a cons-cell of the region. 714 then this function returns a cons-cell of the region.
697 715
698 If optional prefix argument is provided, then prompt for message range with 716 If optional prefix argument is provided, then prompt for message range
699 RANGE-PROMPT. A list of messages in that range is returned. 717 with RANGE-PROMPT. A list of messages in that range is returned.
700 718
701 If a MH range is given, say something like last:20, then a list containing 719 If a MH range is given, say something like last:20, then a list
702 the messages in that range is returned. 720 containing the messages in that range is returned.
703 721
704 If DEFAULT non-nil then it is returned. 722 If DEFAULT non-nil then it is returned.
705 723
706 Otherwise, the message number at point is returned. 724 Otherwise, the message number at point is returned.
707 725
708 This function is usually used with `mh-iterate-on-range' in order to provide 726 This function is usually used with `mh-iterate-on-range' in order to
709 a uniform interface to MH-E functions." 727 provide a uniform interface to MH-E functions."
710 (cond ((mh-mark-active-p t) (cons (region-beginning) (region-end))) 728 (cond ((mh-mark-active-p t) (cons (region-beginning) (region-end)))
711 (current-prefix-arg (mh-read-range range-prompt nil nil t t)) 729 (current-prefix-arg (mh-read-range range-prompt nil nil t t))
712 (default default) 730 (default default)
713 (t (mh-get-msg-num t)))) 731 (t (mh-get-msg-num t))))
714 732
718 736
719 ;; XXX: The function mh-subject-to-sequence-unthreaded uses the magic number 737 ;; XXX: The function mh-subject-to-sequence-unthreaded uses the magic number
720 ;; 41 for the max size of the subject part. Avoiding this would be desirable. 738 ;; 41 for the max size of the subject part. Avoiding this would be desirable.
721 (defun mh-subject-to-sequence (all) 739 (defun mh-subject-to-sequence (all)
722 "Put all following messages with same subject in sequence 'subject. 740 "Put all following messages with same subject in sequence 'subject.
723 If arg ALL is t, move to beginning of folder buffer to collect all messages. 741 If arg ALL is t, move to beginning of folder buffer to collect all
742 messages.
724 If arg ALL is nil, collect only messages fron current one on forward. 743 If arg ALL is nil, collect only messages fron current one on forward.
725 744
726 Return number of messages put in the sequence: 745 Return number of messages put in the sequence:
727 746
728 nil -> there was no subject line. 747 nil -> there was no subject line.
729 0 -> there were no later messages with the same subject (sequence not made) 748
749 0 -> there were no later messages with the same
750 subject (sequence not made)
751
730 >1 -> the total number of messages including current one." 752 >1 -> the total number of messages including current one."
731 (if (memq 'unthread mh-view-ops) 753 (if (memq 'unthread mh-view-ops)
732 (mh-subject-to-sequence-threaded all) 754 (mh-subject-to-sequence-threaded all)
733 (mh-subject-to-sequence-unthreaded all))) 755 (mh-subject-to-sequence-unthreaded all)))
734 756
735 (defun mh-subject-to-sequence-unthreaded (all) 757 (defun mh-subject-to-sequence-unthreaded (all)
736 "Put all following messages with same subject in sequence 'subject. 758 "Put all following messages with same subject in sequence 'subject.
737 This function only works with an unthreaded folder. If arg ALL is t, move to 759
738 beginning of folder buffer to collect all messages. If arg ALL is nil, collect 760 This function only works with an unthreaded folder. If arg ALL is
739 only messages fron current one on forward. 761 t, move to beginning of folder buffer to collect all messages. If
762 arg ALL is nil, collect only messages fron current one on
763 forward.
740 764
741 Return number of messages put in the sequence: 765 Return number of messages put in the sequence:
742 766
743 nil -> there was no subject line. 767 nil -> there was no subject line.
744 0 -> there were no later messages with the same subject (sequence not made) 768 0 -> there were no later messages with the same
769 subject (sequence not made)
745 >1 -> the total number of messages including current one." 770 >1 -> the total number of messages including current one."
746 (if (not (eq major-mode 'mh-folder-mode)) 771 (if (not (eq major-mode 'mh-folder-mode))
747 (error "Not in a folder buffer")) 772 (error "Not in a folder buffer"))
748 (save-excursion 773 (save-excursion
749 (beginning-of-line) 774 (beginning-of-line)
780 (t 805 (t
781 0)))))) 806 0))))))
782 807
783 (defun mh-subject-to-sequence-threaded (all) 808 (defun mh-subject-to-sequence-threaded (all)
784 "Put all messages with the same subject in the 'subject sequence. 809 "Put all messages with the same subject in the 'subject sequence.
785 This function works when the folder is threaded. In this situation the subject 810
786 could get truncated and so the normal matching doesn't work. 811 This function works when the folder is threaded. In this
787 812 situation the subject could get truncated and so the normal
788 The parameter ALL is non-nil then all the messages in the buffer are 813 matching doesn't work.
789 considered, otherwise only the messages after the current one are taken into 814
790 account." 815 The parameter ALL is non-nil then all the messages in the buffer
816 are considered, otherwise only the messages after the current one
817 are taken into account."
791 (let* ((cur (mh-get-msg-num nil)) 818 (let* ((cur (mh-get-msg-num nil))
792 (subject (mh-thread-find-msg-subject cur)) 819 (subject (mh-thread-find-msg-subject cur))
793 region msgs) 820 region msgs)
794 (if (null subject) 821 (if (null subject)
795 (and (message "No subject line") nil) 822 (and (message "No subject line") nil)
822 default-string)) 849 default-string))
823 default))) 850 default)))
824 851
825 (defun mh-pick-args-list (s) 852 (defun mh-pick-args-list (s)
826 "Form list by grouping elements in string S suitable for pick arguments. 853 "Form list by grouping elements in string S suitable for pick arguments.
827 For example, the string \"-subject a b c -from Joe User <user@domain.com>\" 854 For example, the string \"-subject a b c -from Joe User
828 is converted to (\"-subject\" \"a b c\" \"-from\" 855 <user@domain.com>\" is converted to (\"-subject\" \"a b c\"
829 \"Joe User <user@domain.com>\"" 856 \"-from\" \"Joe User <user@domain.com>\""
830 (let ((full-list (split-string s)) 857 (let ((full-list (split-string s))
831 current-arg collection arg-list) 858 current-arg collection arg-list)
832 (while full-list 859 (while full-list
833 (setq current-arg (car full-list)) 860 (setq current-arg (car full-list))
834 (if (null (string-match "^-" current-arg)) 861 (if (null (string-match "^-" current-arg))
933 960
934 ;;;###mh-autoload 961 ;;;###mh-autoload
935 (defun mh-narrow-to-range (range) 962 (defun mh-narrow-to-range (range)
936 "Limit to RANGE. 963 "Limit to RANGE.
937 964
938 Check the documentation of `mh-interactive-range' to see how RANGE is read in 965 Check the documentation of `mh-interactive-range' to see how
939 interactive use. 966 RANGE is read in interactive use.
940 967
941 Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command." 968 Use \\<mh-folder-mode-map>\\[mh-widen] to undo this command."
942 (interactive (list (mh-interactive-range "Narrow to"))) 969 (interactive (list (mh-interactive-range "Narrow to")))
943 (when (assoc 'range mh-seq-list) (mh-delete-seq 'range)) 970 (when (assoc 'range mh-seq-list) (mh-delete-seq 'range))
944 (mh-add-msgs-to-seq (mh-range-to-msg-list range) 'range) 971 (mh-add-msgs-to-seq (mh-range-to-msg-list range) 'range)
947 974
948 ;;;###mh-autoload 975 ;;;###mh-autoload
949 (defun mh-delete-subject () 976 (defun mh-delete-subject ()
950 "Delete messages with same subject\\<mh-folder-mode-map>. 977 "Delete messages with same subject\\<mh-folder-mode-map>.
951 978
952 To delete messages faster, you can use this command to delete all the messages 979 To delete messages faster, you can use this command to delete all
953 with the same subject as the current message. This command puts these messages 980 the messages with the same subject as the current message. This
954 in a sequence named \"subject\". You can undo this action by using \\[mh-undo] 981 command puts these messages in a sequence named \"subject\". You
955 with a prefix argument and then specifying the \"subject\" sequence." 982 can undo this action by using \\[mh-undo] with a prefix argument
983 and then specifying the \"subject\" sequence."
956 (interactive) 984 (interactive)
957 (let ((count (mh-subject-to-sequence nil))) 985 (let ((count (mh-subject-to-sequence nil)))
958 (cond 986 (cond
959 ((not count) ; No subject line, delete msg anyway 987 ((not count) ; No subject line, delete msg anyway
960 (mh-delete-msg (mh-get-msg-num t))) 988 (mh-delete-msg (mh-get-msg-num t)))
967 995
968 ;;;###mh-autoload 996 ;;;###mh-autoload
969 (defun mh-delete-subject-or-thread () 997 (defun mh-delete-subject-or-thread ()
970 "Delete messages with same subject or thread\\<mh-folder-mode-map>. 998 "Delete messages with same subject or thread\\<mh-folder-mode-map>.
971 999
972 To delete messages faster, you can use this command to delete all the messages 1000 To delete messages faster, you can use this command to delete all
973 with the same subject as the current message. This command puts these messages 1001 the messages with the same subject as the current message. This
974 in a sequence named \"subject\". You can undo this action by using \\[mh-undo] 1002 command puts these messages in a sequence named \"subject\". You
975 with a prefix argument and then specifying the \"subject\" sequence. 1003 can undo this action by using \\[mh-undo] with a prefix argument
976 1004 and then specifying the \"subject\" sequence.
977 However, if the buffer is displaying a threaded view of the folder then this 1005
978 command behaves like \\[mh-thread-delete]." 1006 However, if the buffer is displaying a threaded view of the
1007 folder then this command behaves like \\[mh-thread-delete]."
979 (interactive) 1008 (interactive)
980 (if (memq 'unthread mh-view-ops) 1009 (if (memq 'unthread mh-view-ops)
981 (mh-thread-delete) 1010 (mh-thread-delete)
982 (mh-delete-subject))) 1011 (mh-delete-subject)))
983 1012
1003 (mh-thread-initialize-hash mh-thread-duplicates #'eq) 1032 (mh-thread-initialize-hash mh-thread-duplicates #'eq)
1004 (setq mh-thread-history ())) 1033 (setq mh-thread-history ()))
1005 1034
1006 (defsubst mh-thread-id-container (id) 1035 (defsubst mh-thread-id-container (id)
1007 "Given ID, return the corresponding container in `mh-thread-id-table'. 1036 "Given ID, return the corresponding container in `mh-thread-id-table'.
1008 If no container exists then a suitable container is created and the id-table 1037 If no container exists then a suitable container is created and
1009 is updated." 1038 the id-table is updated."
1010 (when (not id) 1039 (when (not id)
1011 (error "1")) 1040 (error "1"))
1012 (or (gethash id mh-thread-id-table) 1041 (or (gethash id mh-thread-id-table)
1013 (setf (gethash id mh-thread-id-table) 1042 (setf (gethash id mh-thread-id-table)
1014 (let ((message (mh-thread-make-message :id id))) 1043 (let ((message (mh-thread-make-message :id id)))
1025 unless (eq child-container elem) collect elem)) 1054 unless (eq child-container elem) collect elem))
1026 (setf (mh-container-parent child-container) nil)))) 1055 (setf (mh-container-parent child-container) nil))))
1027 1056
1028 (defsubst mh-thread-add-link (parent child &optional at-end-p) 1057 (defsubst mh-thread-add-link (parent child &optional at-end-p)
1029 "Add links so that PARENT becomes a parent of CHILD. 1058 "Add links so that PARENT becomes a parent of CHILD.
1030 Doesn't make any changes if CHILD is already an ancestor of PARENT. If 1059 Doesn't make any changes if CHILD is already an ancestor of
1031 optional argument AT-END-P is non-nil, the CHILD is added to the end of the 1060 PARENT. If optional argument AT-END-P is non-nil, the CHILD is
1032 children list of PARENT." 1061 added to the end of the children list of PARENT."
1033 (let ((parent-container (cond ((null parent) nil) 1062 (let ((parent-container (cond ((null parent) nil)
1034 ((mh-thread-container-p parent) parent) 1063 ((mh-thread-container-p parent) parent)
1035 (t (mh-thread-id-container parent)))) 1064 (t (mh-thread-id-container parent))))
1036 (child-container (if (mh-thread-container-p child) 1065 (child-container (if (mh-thread-container-p child)
1037 child (mh-thread-id-container child)))) 1066 child (mh-thread-id-container child))))
1051 (unless parent-container 1080 (unless parent-container
1052 (mh-thread-remove-parent-link child-container)))) 1081 (mh-thread-remove-parent-link child-container))))
1053 1082
1054 (defun mh-thread-ancestor-p (ancestor successor) 1083 (defun mh-thread-ancestor-p (ancestor successor)
1055 "Return t if ANCESTOR is really an ancestor of SUCCESSOR and nil otherwise. 1084 "Return t if ANCESTOR is really an ancestor of SUCCESSOR and nil otherwise.
1056 In the limit, the function returns t if ANCESTOR and SUCCESSOR are the same 1085 In the limit, the function returns t if ANCESTOR and SUCCESSOR
1057 containers." 1086 are the same containers."
1058 (block nil 1087 (block nil
1059 (while successor 1088 (while successor
1060 (when (eq ancestor successor) (return t)) 1089 (when (eq ancestor successor) (return t))
1061 (setq successor (mh-container-parent successor))) 1090 (setq successor (mh-container-parent successor)))
1062 nil)) 1091 nil))
1063 1092
1064 (defsubst mh-thread-get-message-container (message) 1093 (defsubst mh-thread-get-message-container (message)
1065 "Return container which has MESSAGE in it. 1094 "Return container which has MESSAGE in it.
1066 If there is no container present then a new container is allocated." 1095 If there is no container present then a new container is
1096 allocated."
1067 (let* ((id (mh-message-id message)) 1097 (let* ((id (mh-message-id message))
1068 (container (gethash id mh-thread-id-table))) 1098 (container (gethash id mh-thread-id-table)))
1069 (cond (container (setf (mh-container-message container) message) 1099 (cond (container (setf (mh-container-message container) message)
1070 container) 1100 container)
1071 (t (setf (gethash id mh-thread-id-table) 1101 (t (setf (gethash id mh-thread-id-table)
1072 (mh-thread-make-container :message message)))))) 1102 (mh-thread-make-container :message message))))))
1073 1103
1074 (defsubst mh-thread-get-message (id subject-re-p subject refs) 1104 (defsubst mh-thread-get-message (id subject-re-p subject refs)
1075 "Return appropriate message. 1105 "Return appropriate message.
1076 Otherwise update message already present to have the proper ID, SUBJECT-RE-P, 1106 Otherwise update message already present to have the proper ID,
1077 SUBJECT and REFS fields." 1107 SUBJECT-RE-P, SUBJECT and REFS fields."
1078 (let* ((container (gethash id mh-thread-id-table)) 1108 (let* ((container (gethash id mh-thread-id-table))
1079 (message (if container (mh-container-message container) nil))) 1109 (message (if container (mh-container-message container) nil)))
1080 (cond (message 1110 (cond (message
1081 (setf (mh-message-subject-re-p message) subject-re-p) 1111 (setf (mh-message-subject-re-p message) subject-re-p)
1082 (setf (mh-message-subject message) subject) 1112 (setf (mh-message-subject message) subject)
1101 (gethash id mh-thread-id-hash) 1131 (gethash id mh-thread-id-hash)
1102 (setf (gethash id mh-thread-id-hash) id))) 1132 (setf (gethash id mh-thread-id-hash) id)))
1103 1133
1104 (defsubst mh-thread-prune-subject (subject) 1134 (defsubst mh-thread-prune-subject (subject)
1105 "Prune leading Re:'s, Fwd:'s etc. and trailing (fwd)'s from SUBJECT. 1135 "Prune leading Re:'s, Fwd:'s etc. and trailing (fwd)'s from SUBJECT.
1106 If the result after pruning is not the empty string then it is canonicalized 1136 If the result after pruning is not the empty string then it is
1107 so that subjects can be tested for equality with eq. This is done so that all 1137 canonicalized so that subjects can be tested for equality with
1108 the messages without a subject are not put into a single thread." 1138 eq. This is done so that all the messages without a subject are
1139 not put into a single thread."
1109 (let ((case-fold-search t) 1140 (let ((case-fold-search t)
1110 (subject-pruned-flag nil)) 1141 (subject-pruned-flag nil))
1111 ;; Prune subject leader 1142 ;; Prune subject leader
1112 (while (or (string-match "^[ \t]*\\(re\\|fwd?\\)\\(\\[[0-9]*\\]\\)?:[ \t]*" 1143 (while (or (string-match "^[ \t]*\\(re\\|fwd?\\)\\(\\[[0-9]*\\]\\)?:[ \t]*"
1113 subject) 1144 subject)
1126 (setf (gethash subject mh-thread-subject-hash) subject)) 1157 (setf (gethash subject mh-thread-subject-hash) subject))
1127 subject-pruned-flag))))) 1158 subject-pruned-flag)))))
1128 1159
1129 (defun mh-thread-container-subject (container) 1160 (defun mh-thread-container-subject (container)
1130 "Return the subject of CONTAINER. 1161 "Return the subject of CONTAINER.
1131 If CONTAINER is empty return the subject info of one of its children." 1162 If CONTAINER is empty return the subject info of one of its
1163 children."
1132 (cond ((and (mh-container-message container) 1164 (cond ((and (mh-container-message container)
1133 (mh-message-id (mh-container-message container))) 1165 (mh-message-id (mh-container-message container)))
1134 (mh-message-subject (mh-container-message container))) 1166 (mh-message-subject (mh-container-message container)))
1135 (t (block nil 1167 (t (block nil
1136 (dolist (kid (mh-container-children container)) 1168 (dolist (kid (mh-container-children container))
1231 (and (integerp index-x) (integerp index-y) 1263 (and (integerp index-x) (integerp index-y)
1232 (< index-x index-y))))))) 1264 (< index-x index-y)))))))
1233 1265
1234 (defsubst mh-thread-group-by-subject (roots) 1266 (defsubst mh-thread-group-by-subject (roots)
1235 "Group the set of message containers, ROOTS based on subject. 1267 "Group the set of message containers, ROOTS based on subject.
1236 Bug: Check for and make sure that something without Re: is made the parent in 1268 Bug: Check for and make sure that something without Re: is made
1237 preference to something that has it." 1269 the parent in preference to something that has it."
1238 (clrhash mh-thread-subject-container-hash) 1270 (clrhash mh-thread-subject-container-hash)
1239 (let ((results ())) 1271 (let ((results ()))
1240 (dolist (root roots) 1272 (dolist (root roots)
1241 (let* ((subject (mh-thread-container-subject root)) 1273 (let* ((subject (mh-thread-container-subject root))
1242 (parent (gethash subject mh-thread-subject-container-hash))) 1274 (parent (gethash subject mh-thread-subject-container-hash)))
1249 (push root results))))) 1281 (push root results)))))
1250 (nreverse results))) 1282 (nreverse results)))
1251 1283
1252 (defun mh-thread-process-in-reply-to (reply-to-header) 1284 (defun mh-thread-process-in-reply-to (reply-to-header)
1253 "Extract message id's from REPLY-TO-HEADER. 1285 "Extract message id's from REPLY-TO-HEADER.
1254 Ideally this should have some regexp which will try to guess if a string 1286 Ideally this should have some regexp which will try to guess if a
1255 between < and > is a message id and not an email address. For now it will 1287 string between < and > is a message id and not an email address.
1256 take the last string inside angles." 1288 For now it will take the last string inside angles."
1257 (let ((end (mh-search-from-end ?> reply-to-header))) 1289 (let ((end (mh-search-from-end ?> reply-to-header)))
1258 (when (numberp end) 1290 (when (numberp end)
1259 (let ((begin (mh-search-from-end ?< (substring reply-to-header 0 end)))) 1291 (let ((begin (mh-search-from-end ?< (substring reply-to-header 0 end))))
1260 (when (numberp begin) 1292 (when (numberp begin)
1261 (list (substring reply-to-header begin (1+ end)))))))) 1293 (list (substring reply-to-header begin (1+ end))))))))
1277 (setq mh-thread-duplicates (mh-get-table 'mh-thread-duplicates)) 1309 (setq mh-thread-duplicates (mh-get-table 'mh-thread-duplicates))
1278 (setq mh-thread-history (mh-get-table 'mh-thread-history)))) 1310 (setq mh-thread-history (mh-get-table 'mh-thread-history))))
1279 1311
1280 (defsubst mh-thread-update-id-index-maps (id index) 1312 (defsubst mh-thread-update-id-index-maps (id index)
1281 "Message with id, ID is the message in INDEX. 1313 "Message with id, ID is the message in INDEX.
1282 The function also checks for duplicate messages (that is multiple messages 1314 The function also checks for duplicate messages (that is multiple
1283 with the same ID). These messages are put in the `mh-thread-duplicates' hash 1315 messages with the same ID). These messages are put in the
1284 table." 1316 `mh-thread-duplicates' hash table."
1285 (let ((old-index (gethash id mh-thread-id-index-map))) 1317 (let ((old-index (gethash id mh-thread-id-index-map)))
1286 (when old-index (push old-index (gethash id mh-thread-duplicates))) 1318 (when old-index (push old-index (gethash id mh-thread-duplicates)))
1287 (setf (gethash id mh-thread-id-index-map) index) 1319 (setf (gethash id mh-thread-id-index-map) index)
1288 (setf (gethash index mh-thread-index-id-map) id))) 1320 (setf (gethash index mh-thread-index-id-map) id)))
1289 1321
1381 (mh-notate-cur) 1413 (mh-notate-cur)
1382 (set-buffer-modified-p old-buffer-modified-flag)))) 1414 (set-buffer-modified-p old-buffer-modified-flag))))
1383 1415
1384 (defun mh-thread-generate-scan-lines (tree level) 1416 (defun mh-thread-generate-scan-lines (tree level)
1385 "Generate scan lines. 1417 "Generate scan lines.
1386 TREE is the hierarchical tree of messages, SCAN-LINE-MAP maps message indices 1418 TREE is the hierarchical tree of messages, SCAN-LINE-MAP maps
1387 to the corresponding scan lines and LEVEL used to determine indentation of 1419 message indices to the corresponding scan lines and LEVEL used to
1388 the message." 1420 determine indentation of the message."
1389 (cond ((null tree) nil) 1421 (cond ((null tree) nil)
1390 ((mh-thread-container-p tree) 1422 ((mh-thread-container-p tree)
1391 (let* ((message (mh-container-message tree)) 1423 (let* ((message (mh-container-message tree))
1392 (id (mh-message-id message)) 1424 (id (mh-message-id message))
1393 (index (gethash id mh-thread-id-index-map)) 1425 (index (gethash id mh-thread-id-index-map))
1434 1466
1435 ;; Another and may be better approach would be to generate all the info from 1467 ;; Another and may be better approach would be to generate all the info from
1436 ;; the scan which generates the threading info. For now this will have to do. 1468 ;; the scan which generates the threading info. For now this will have to do.
1437 (defun mh-thread-parse-scan-line (&optional string) 1469 (defun mh-thread-parse-scan-line (&optional string)
1438 "Parse a scan line. 1470 "Parse a scan line.
1439 If optional argument STRING is given then that is assumed to be the scan line. 1471 If optional argument STRING is given then that is assumed to be
1440 Otherwise uses the line at point as the scan line to parse." 1472 the scan line. Otherwise uses the line at point as the scan line
1473 to parse."
1441 (let* ((string (or string 1474 (let* ((string (or string
1442 (buffer-substring-no-properties (line-beginning-position) 1475 (buffer-substring-no-properties (line-beginning-position)
1443 (line-end-position)))) 1476 (line-end-position))))
1444 (address-start (+ mh-cmd-note mh-scan-field-from-start-offset)) 1477 (address-start (+ mh-cmd-note mh-scan-field-from-start-offset))
1445 (body-start (+ mh-cmd-note mh-scan-field-from-end-offset)) 1478 (body-start (+ mh-cmd-note mh-scan-field-from-end-offset))
1581 1614
1582 ;;;###mh-autoload 1615 ;;;###mh-autoload
1583 (defun mh-thread-next-sibling (&optional previous-flag) 1616 (defun mh-thread-next-sibling (&optional previous-flag)
1584 "Display next sibling. 1617 "Display next sibling.
1585 1618
1586 With non-nil optional argument PREVIOUS-FLAG jump to the previous sibling." 1619 With non-nil optional argument PREVIOUS-FLAG jump to the previous
1620 sibling."
1587 (interactive) 1621 (interactive)
1588 (cond ((not (memq 'unthread mh-view-ops)) 1622 (cond ((not (memq 'unthread mh-view-ops))
1589 (error "Folder isn't threaded")) 1623 (error "Folder isn't threaded"))
1590 ((eobp) 1624 ((eobp)
1591 (error "No message at point"))) 1625 (error "No message at point")))
1630 1664
1631 ;;;###mh-autoload 1665 ;;;###mh-autoload
1632 (defun mh-thread-ancestor (&optional thread-root-flag) 1666 (defun mh-thread-ancestor (&optional thread-root-flag)
1633 "Display ancestor of current message. 1667 "Display ancestor of current message.
1634 1668
1635 If you do not care for the way a particular thread has turned, you can move up 1669 If you do not care for the way a particular thread has turned,
1636 the chain of messages with this command. This command can also take a prefix 1670 you can move up the chain of messages with this command. This
1637 argument THREAD-ROOT-FLAG to jump to the message that started everything." 1671 command can also take a prefix argument THREAD-ROOT-FLAG to jump
1672 to the message that started everything."
1638 (interactive "P") 1673 (interactive "P")
1639 (beginning-of-line) 1674 (beginning-of-line)
1640 (cond ((not (memq 'unthread mh-view-ops)) 1675 (cond ((not (memq 'unthread mh-view-ops))
1641 (error "Folder isn't threaded")) 1676 (error "Folder isn't threaded"))
1642 ((eobp) 1677 ((eobp)
1650 (t (mh-thread-immediate-ancestor) 1685 (t (mh-thread-immediate-ancestor)
1651 (mh-maybe-show))))) 1686 (mh-maybe-show)))))
1652 1687
1653 (defun mh-thread-find-children () 1688 (defun mh-thread-find-children ()
1654 "Return a region containing the current message and its children. 1689 "Return a region containing the current message and its children.
1655 The result is returned as a list of two elements. The first is the point at the 1690 The result is returned as a list of two elements. The first is
1656 start of the region and the second is the point at the end." 1691 the point at the start of the region and the second is the point
1692 at the end."
1657 (beginning-of-line) 1693 (beginning-of-line)
1658 (if (eobp) 1694 (if (eobp)
1659 nil 1695 nil
1660 (let ((address-start-offset (+ mh-cmd-note mh-scan-date-flag-width 1696 (let ((address-start-offset (+ mh-cmd-note mh-scan-date-flag-width
1661 mh-scan-date-width 1)) 1697 mh-scan-date-width 1))
1742 1778
1743 ;;;###mh-autoload 1779 ;;;###mh-autoload
1744 (defun mh-narrow-to-tick () 1780 (defun mh-narrow-to-tick ()
1745 "Limit to ticked messages. 1781 "Limit to ticked messages.
1746 1782
1747 What this command does is show only those messages that are in the \"tick\" 1783 What this command does is show only those messages that are in
1748 sequence (which you can customize via the `mh-tick-seq' option) in the 1784 the \"tick\" sequence (which you can customize via the
1749 MH-Folder buffer. In addition, it limits further MH-E searches to just those 1785 `mh-tick-seq' option) in the MH-Folder buffer. In addition, it
1750 messages. When you want to widen the view to all your messages again, use 1786 limits further MH-E searches to just those messages. When you
1787 want to widen the view to all your messages again, use
1751 \\[mh-widen]." 1788 \\[mh-widen]."
1752 (interactive) 1789 (interactive)
1753 (cond ((not mh-tick-seq) 1790 (cond ((not mh-tick-seq)
1754 (error "Enable ticking by customizing `mh-tick-seq'")) 1791 (error "Enable ticking by customizing `mh-tick-seq'"))
1755 ((null (mh-seq-msgs (mh-find-seq mh-tick-seq))) 1792 ((null (mh-seq-msgs (mh-find-seq mh-tick-seq)))