(require 'gnus-range)
(require 'gnus-int)
(require 'gnus-undo)
+(require 'gnus-util)
(autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
(defcustom gnus-kill-summary-on-exit t
:group 'gnus-thread
:type 'boolean)
-(defcustom gnus-thread-ignore-subject nil
- "*If non-nil, ignore subjects and do all threading based on the Reference header.
-If nil, which is the default, articles that have different subjects
-from their parents will start separate threads."
+(defcustom gnus-thread-ignore-subject t
+ "*If non-nil, which is the default, ignore subjects and do all threading based on the Reference header.
+If nil, articles that have different subjects from their parents will
+start separate threads."
:group 'gnus-thread
:type 'boolean)
(sexp :menu-tag "on" t)))
(defcustom gnus-auto-select-same nil
- "*If non-nil, select the next article with the same subject."
+ "*If non-nil, select the next article with the same subject.
+If there are no more articles with the same subject, go to
+the first unread article."
:group 'gnus-summary-maneuvering
:type 'boolean)
"b" gnus-article-hide-boring-headers
"s" gnus-article-hide-signature
"c" gnus-article-hide-citation
+ "C" gnus-article-hide-citation-in-followups
"p" gnus-article-hide-pgp
"P" gnus-article-hide-pem
"\C-c" gnus-article-hide-citation-maybe)
"c" gnus-summary-copy-article
"B" gnus-summary-crosspost-article
"q" gnus-summary-respool-query
+ "t" gnus-summary-respool-trace
"i" gnus-summary-import-article
"p" gnus-summary-article-posted-p)
(gnus-check-backend-function
'request-expire-articles gnus-newsgroup-name)]
["Query respool" gnus-summary-respool-query t]
+ ["Trace respool" gnus-summary-respool-trace t]
["Delete expirable articles" gnus-summary-expire-articles-now
(gnus-check-backend-function
'request-expire-articles gnus-newsgroup-name)])
["Edit local kill file" gnus-summary-edit-local-kill t]
["Edit main kill file" gnus-summary-edit-global-kill t]
["Edit group parameters" gnus-summary-edit-parameters t]
+ ["Send a bug report" gnus-bug t]
("Exit"
["Catchup and exit" gnus-summary-catchup-and-exit t]
["Catchup all and exit" gnus-summary-catchup-and-exit t]
(when list
(let ((data (and after-article (gnus-data-find-list after-article)))
(ilist list))
- (or data (not after-article) (error "No such article: %d" after-article))
- ;; Find the last element in the list to be spliced into the main
- ;; list.
- (while (cdr list)
- (setq list (cdr list)))
- (if (not data)
- (progn
- (setcdr list gnus-newsgroup-data)
- (setq gnus-newsgroup-data ilist)
+ (if (not (or data
+ after-article))
+ (let ((odata gnus-newsgroup-data))
+ (setq gnus-newsgroup-data (nconc list gnus-newsgroup-data))
(when offset
- (gnus-data-update-list (cdr list) offset)))
- (setcdr list (cdr data))
- (setcdr data ilist)
- (when offset
- (gnus-data-update-list (cdr list) offset)))
+ (gnus-data-update-list odata offset)))
+ ;; Find the last element in the list to be spliced into the main
+ ;; list.
+ (while (cdr list)
+ (setq list (cdr list)))
+ (if (not data)
+ (progn
+ (setcdr list gnus-newsgroup-data)
+ (setq gnus-newsgroup-data ilist)
+ (when offset
+ (gnus-data-update-list (cdr list) offset)))
+ (setcdr list (cdr data))
+ (setcdr data ilist)
+ (when offset
+ (gnus-data-update-list (cdr list) offset))))
(setq gnus-newsgroup-data-reverse nil))))
(defun gnus-data-remove (article &optional offset)
(defun gnus-data-update-list (data offset)
"Add OFFSET to the POS of all data entries in DATA."
+ (setq gnus-newsgroup-data-reverse nil)
(while data
(setcar (nthcdr 2 (car data)) (+ offset (nth 2 (car data))))
(setq data (cdr data))))
(defun gnus-data-compute-positions ()
"Compute the positions of all articles."
+ (setq gnus-newsgroup-data-reverse nil)
(let ((data gnus-newsgroup-data)
pos)
(while data
(setq gnus-tmp-name gnus-tmp-from))
(unless (numberp gnus-tmp-lines)
(setq gnus-tmp-lines 0))
- (gnus-put-text-property
+ (gnus-put-text-property-excluding-characters-with-faces
(point)
(progn (eval gnus-summary-line-format-spec) (point))
'gnus-number gnus-tmp-number)
(set (car elem) (eval (nth 1 elem))))))))
(defun gnus-summary-read-group (group &optional show-all no-article
- kill-buffer no-display backward)
+ kill-buffer no-display backward
+ select-articles)
"Start reading news in newsgroup GROUP.
If SHOW-ALL is non-nil, already read articles are also listed.
If NO-ARTICLE is non-nil, no article is selected initially.
(let ((gnus-auto-select-next nil))
(or (gnus-summary-read-group-1
group show-all no-article
- kill-buffer no-display)
- (setq show-all nil)))))
+ kill-buffer no-display
+ select-articles)
+ (setq show-all nil
+ select-articles nil)))))
(eq gnus-auto-select-next 'quietly))
(set-buffer gnus-group-buffer)
;; The entry function called above goes to the next
result))
(defun gnus-summary-read-group-1 (group show-all no-article
- kill-buffer no-display)
+ kill-buffer no-display
+ &optional select-articles)
;; Killed foreign groups can't be entered.
(when (and (not (gnus-group-native-p group))
(not (gnus-gethash group gnus-newsrc-hashtb)))
(gnus-message 5 "Retrieving newsgroup: %s..." group)
(let* ((new-group (gnus-summary-setup-buffer group))
(quit-config (gnus-group-quit-config group))
- (did-select (and new-group (gnus-select-newsgroup group show-all))))
+ (did-select (and new-group (gnus-select-newsgroup
+ group show-all select-articles))))
(cond
;; This summary buffer exists already, so we just select it.
((not new-group)
;; Yuk! This is a reference loop. Make the article be a
;; root article.
(progn
- (debug)
(mail-header-set-references (car (symbol-value id-dep)) "none")
(setq ref nil))
(setq ref (gnus-parent-id (mail-header-references ref-header)))))
(defun gnus-build-sparse-threads ()
(let ((headers gnus-newsgroup-headers)
+ (gnus-summary-ignore-duplicates t)
header references generation relations
cthread subject child end pthread relation new-child date)
;; First we create an alist of generations/relations, where
generation 0)
(while (search-backward ">" nil t)
(setq end (1+ (point)))
- (if (search-backward "<" nil t)
- (push (list (incf generation)
- child (setq child new-child)
- subject date)
- relations)))
- (push (list (1+ generation) child nil subject) relations)
+ (when (search-backward "<" nil t)
+ (setq new-child (buffer-substring (point) end))
+ (push (list (incf generation)
+ child (setq child new-child)
+ subject date)
+ relations)))
+ (when child
+ (push (list (1+ generation) child nil subject) relations))
(erase-buffer)))
(kill-buffer (current-buffer)))
;; Sort over trustworthiness.
(when (gnus-dependencies-add-header
(make-full-mail-header
gnus-reffed-article-number
- (nth 3 relation) "" (nth 4 relation)
+ (nth 3 relation) "" (or (nth 4 relation) "")
(nth 1 relation)
(or (nth 2 relation) "") 0 0 "")
gnus-newsgroup-dependencies nil)
(defun gnus-summary-update-article (article &optional iheader)
"Update ARTICLE in the summary buffer."
(set-buffer gnus-summary-buffer)
- (let* ((header (or iheader (gnus-summary-article-header article)))
+ (let* ((header (gnus-summary-article-header article))
(id (mail-header-id header))
(data (gnus-data-find article))
(thread (gnus-id-to-thread id))
(number (mail-header-number header))
pos)
(when thread
- ;; !!! Should this be in or not?
(unless iheader
- (setcar thread nil))
- (when parent
- (delq thread parent))
- (if (gnus-summary-insert-subject id header iheader)
+ (setcar thread nil)
+ (when parent
+ (delq thread parent)))
+ (if (gnus-summary-insert-subject id header)
;; Set the (possibly) new article number in the data structure.
(gnus-data-set-number data (gnus-id-to-article id))
(setcar thread old)
;;!!! then we want to insert at the beginning of the buffer.
;;!!! That happens to be true with Gnus now, but that may
;;!!! change in the future. Perhaps.
- (gnus-data-enter-list (if line nil current) data (- (point) old-pos))
- (setq gnus-newsgroup-threads (nconc threads gnus-newsgroup-threads))
- (when line
- (gnus-data-compute-positions)))))
+ (gnus-data-enter-list
+ (if line nil current) data (- (point) old-pos))
+ (setq gnus-newsgroup-threads
+ (nconc threads gnus-newsgroup-threads))
+ (gnus-data-compute-positions))))
(defun gnus-number-to-header (number)
"Return the header for article NUMBER."
(setq gnus-tmp-name gnus-tmp-from))
(unless (numberp gnus-tmp-lines)
(setq gnus-tmp-lines 0))
- (gnus-put-text-property
+ (gnus-put-text-property-excluding-characters-with-faces
(point)
(progn (eval gnus-summary-line-format-spec) (point))
'gnus-number number)
(cdr (assq number gnus-newsgroup-scored))
(memq number gnus-newsgroup-processable))))))
-(defun gnus-select-newsgroup (group &optional read-all)
+(defun gnus-select-newsgroup (group &optional read-all select-articles)
"Select newsgroup GROUP.
-If READ-ALL is non-nil, all articles in the group are selected."
+If READ-ALL is non-nil, all articles in the group are selected.
+If SELECT-ARTICLES, only select those articles from GROUP."
(let* ((entry (gnus-gethash group gnus-newsrc-hashtb))
;;!!! Dirty hack; should be removed.
(gnus-summary-ignore-duplicates
(unless (gnus-ephemeral-group-p gnus-newsgroup-name)
(gnus-group-update-group group))
- (setq articles (gnus-articles-to-read group read-all))
+ (if (setq articles select-articles)
+ (setq gnus-newsgroup-unselected
+ (gnus-sorted-intersection
+ gnus-newsgroup-unreads
+ (gnus-sorted-complement gnus-newsgroup-unreads articles)))
+ (setq articles (gnus-articles-to-read group read-all)))
(cond
((null articles)
;; Removed marked articles that do not exist.
(gnus-update-missing-marks
(gnus-sorted-complement fetched-articles articles))
- ;; Let the Gnus agent mark articles as read.
- (when gnus-agent
- (gnus-agent-get-undownloaded-list))
;; We might want to build some more threads first.
(when (and gnus-fetch-old-headers
(eq gnus-headers-retrieved-by 'nov))
(if (eq gnus-fetch-old-headers 'invisible)
(gnus-build-all-threads)
(gnus-build-old-threads)))
+ ;; Let the Gnus agent mark articles as read.
+ (when gnus-agent
+ (gnus-agent-get-undownloaded-list))
;; Check whether auto-expire is to be done in this group.
(setq gnus-newsgroup-auto-expire
(gnus-group-auto-expirable-p group))
;; Just return the current article.
(list (gnus-summary-article-number))))))
+(defmacro gnus-summary-iterate (arg &rest forms)
+ "Iterate over the process/prefixed articles and do FORMS.
+ARG is the interactive prefix given to the command. FORMS will be
+executed with point over the summary line of the articles."
+ (let ((articles (make-symbol "gnus-summary-iterate-articles")))
+ `(let ((,articles (gnus-summary-work-articles ,arg)))
+ (while ,articles
+ (gnus-summary-goto-subject (car ,articles))
+ ,@forms))))
+
+(put 'gnus-summary-iterate 'lisp-indent-function 1)
+(put 'gnus-summary-iterate 'edebug-form-spec '(form body))
+
(defun gnus-summary-save-process-mark ()
"Push the current set of process marked articles on the stack."
(interactive)
;; first unread article is the article after the last read
;; article. Sounds logical, doesn't it?
(if (not (listp (cdr read)))
- (setq first (1+ (cdr read)))
+ (setq first (max (car active) (1+ (cdr read))))
;; `read' is a list of ranges.
(when (/= (setq nlast (or (and (numberp (car read)) (car read))
(caar read)))
1)
- (setq first 1))
+ (setq first (car active)))
(while read
(when first
(while (< first nlast)
(gnus-message 3 "Can't find article %d" article))
nil)
(goto-char (gnus-data-pos data))
+ (gnus-summary-position-point)
article)))
;; Walking around summary lines with displaying articles.
'<)
(sort gnus-newsgroup-limit '<)))
article)
- (setq gnus-newsgroup-unreads gnus-newsgroup-limit)
+ (setq gnus-newsgroup-unreads
+ (gnus-intersection gnus-newsgroup-unreads gnus-newsgroup-limit))
(if all
(setq gnus-newsgroup-dormant nil
gnus-newsgroup-marked nil
;; after the current one.
(goto-char (point-max))
(gnus-summary-find-prev))
+ (gnus-set-mode-line 'summary)
;; We return how many articles were removed from the summary
;; buffer as a result of the new limit.
(- total (length gnus-newsgroup-data))))
"Make edits to the current article permanent."
(interactive)
;; Replace the article.
- (if (and (not read-only)
- (not (gnus-request-replace-article
- (cdr gnus-article-current) (car gnus-article-current)
- (current-buffer))))
- (error "Couldn't replace article")
- ;; Update the summary buffer.
- (if (and references
- (equal (message-tokenize-header references " ")
- (message-tokenize-header
- (or (message-fetch-field "references") "") " ")))
- ;; We only have to update this line.
- (save-excursion
- (save-restriction
- (message-narrow-to-head)
- (let ((head (buffer-string))
- header)
- (nnheader-temp-write nil
- (insert (format "211 %d Article retrieved.\n"
- (cdr gnus-article-current)))
- (insert head)
- (insert ".\n")
- (let ((nntp-server-buffer (current-buffer)))
- (setq header (car (gnus-get-newsgroup-headers
- (save-excursion
- (set-buffer gnus-summary-buffer)
- gnus-newsgroup-dependencies)
- t))))
- (save-excursion
- (set-buffer gnus-summary-buffer)
- (gnus-data-set-header
- (gnus-data-find (cdr gnus-article-current))
- header)
- (gnus-summary-update-article-line
- (cdr gnus-article-current) header))))))
- ;; Update threads.
- (set-buffer (or buffer gnus-summary-buffer))
- (gnus-summary-update-article (cdr gnus-article-current)))
- ;; Prettify the article buffer again.
- (unless no-highlight
- (save-excursion
- (set-buffer gnus-article-buffer)
- (gnus-run-hooks 'gnus-article-display-hook)
- (set-buffer gnus-original-article-buffer)
- (gnus-request-article
- (cdr gnus-article-current)
- (car gnus-article-current) (current-buffer))))
- ;; Prettify the summary buffer line.
- (when (gnus-visual-p 'summary-highlight 'highlight)
- (gnus-run-hooks 'gnus-visual-mark-article-hook))))
+ (let ((buf (current-buffer)))
+ (nnheader-temp-write nil
+ (insert-buffer buf)
+ (if (and (not read-only)
+ (not (gnus-request-replace-article
+ (cdr gnus-article-current) (car gnus-article-current)
+ (current-buffer))))
+ (error "Couldn't replace article")
+ ;; Update the summary buffer.
+ (if (and references
+ (equal (message-tokenize-header references " ")
+ (message-tokenize-header
+ (or (message-fetch-field "references") "") " ")))
+ ;; We only have to update this line.
+ (save-excursion
+ (save-restriction
+ (message-narrow-to-head)
+ (let ((head (buffer-string))
+ header)
+ (nnheader-temp-write nil
+ (insert (format "211 %d Article retrieved.\n"
+ (cdr gnus-article-current)))
+ (insert head)
+ (insert ".\n")
+ (let ((nntp-server-buffer (current-buffer)))
+ (setq header (car (gnus-get-newsgroup-headers
+ (save-excursion
+ (set-buffer gnus-summary-buffer)
+ gnus-newsgroup-dependencies)
+ t))))
+ (save-excursion
+ (set-buffer gnus-summary-buffer)
+ (gnus-data-set-header
+ (gnus-data-find (cdr gnus-article-current))
+ header)
+ (gnus-summary-update-article-line
+ (cdr gnus-article-current) header))))))
+ ;; Update threads.
+ (set-buffer (or buffer gnus-summary-buffer))
+ (gnus-summary-update-article (cdr gnus-article-current)))
+ ;; Prettify the article buffer again.
+ (unless no-highlight
+ (save-excursion
+ (set-buffer gnus-article-buffer)
+ (gnus-run-hooks 'gnus-article-display-hook)
+ (set-buffer gnus-original-article-buffer)
+ (gnus-request-article
+ (cdr gnus-article-current)
+ (car gnus-article-current) (current-buffer))))
+ ;; Prettify the summary buffer line.
+ (when (gnus-visual-p 'summary-highlight 'highlight)
+ (gnus-run-hooks 'gnus-visual-mark-article-hook))))))
(defun gnus-summary-edit-wash (key)
"Perform editing command KEY in the article buffer."
;;; Respooling
-(defun gnus-summary-respool-query (&optional silent)
+(defun gnus-summary-respool-query (&optional silent trace)
"Query where the respool algorithm would put this article."
(interactive)
(let (gnus-mark-article-hook)
(set-buffer gnus-original-article-buffer)
(save-restriction
(message-narrow-to-head)
- (let ((groups (nnmail-article-group 'identity)))
+ (let ((groups (nnmail-article-group 'identity trace)))
(unless silent
(if groups
(message "This message would go to %s"
(message "This message would go to no groups"))
groups))))))
+(defun gnus-summary-respool-trace ()
+ "Trace where the respool algorithm would put this article.
+Display a buffer showing all fancy splitting patterns which matched."
+ (interactive)
+ (gnus-summary-respool-query nil t))
+
;; Summary marking commands.
(defun gnus-summary-kill-same-subject-and-select (&optional unmark)
(setq list (cdr list))))
(let ((face (cdar list)))
(unless (eq face (get-text-property beg 'face))
- (gnus-put-text-property
+ (gnus-put-text-property-excluding-characters-with-faces
beg end 'face
(setq face (if (boundp face) (symbol-value face) face)))
(when gnus-summary-highlight-line-function