1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-06 06:20:55 -08:00

Improve mark handling in gnus nnselect

* lisp/gnus/nnselect.el (numbers-by-group,
nnselect-request-update-info, nnselect-push-info): Handle all three
mark types ('tuple, 'range, 'list) and general speedups.
This commit is contained in:
Andrew G Cohen 2020-09-23 19:47:15 +08:00
parent e4831151c2
commit 6037051f49

View file

@ -203,11 +203,22 @@ as `(keyfunc member)' and the corresponding element is just
(nnselect-categorize ,articles 'nnselect-article-group (nnselect-categorize ,articles 'nnselect-article-group
'nnselect-article-id))) 'nnselect-article-id)))
(define-inline numbers-by-group (articles) (define-inline numbers-by-group (articles &optional type)
(inline-quote (inline-quote
(nnselect-categorize (cond
,articles 'nnselect-article-group 'nnselect-article-number))) ((eq ,type 'range)
(nnselect-categorize (gnus-uncompress-range ,articles)
'nnselect-article-group 'nnselect-article-number))
((eq ,type 'tuple)
(nnselect-categorize ,articles
#'(lambda (elem)
(nnselect-article-group (car elem)))
#'(lambda (elem)
(cons (nnselect-article-number
(car elem)) (cdr elem)))))
(t
(nnselect-categorize ,articles
'nnselect-article-group 'nnselect-article-number)))))
(defmacro nnselect-add-prefix (group) (defmacro nnselect-add-prefix (group)
"Ensures that the GROUP has an nnselect prefix." "Ensures that the GROUP has an nnselect prefix."
@ -504,15 +515,15 @@ If this variable is nil, or if the provided function returns nil,
(list (car artgroup) (list (car artgroup)
(gnus-compress-sequence (sort (cdr artgroup) '<)) (gnus-compress-sequence (sort (cdr artgroup) '<))
action marks)) action marks))
(numbers-by-group (numbers-by-group range 'range))))
(gnus-uncompress-range range)))))
actions) actions)
'car 'cdr))) 'car 'cdr)))
(deffoo nnselect-request-update-info (group info &optional _server) (deffoo nnselect-request-update-info (group info &optional _server)
(let* ((group (nnselect-add-prefix group)) (let* ((group (nnselect-add-prefix group))
(gnus-newsgroup-selection (or gnus-newsgroup-selection (gnus-newsgroup-selection
(nnselect-get-artlist group)))) (or gnus-newsgroup-selection (nnselect-get-artlist group)))
newmarks)
(gnus-info-set-marks info nil) (gnus-info-set-marks info nil)
(setf (gnus-info-read info) nil) (setf (gnus-info-read info) nil)
(pcase-dolist (`(,artgroup . ,nartids) (pcase-dolist (`(,artgroup . ,nartids)
@ -520,30 +531,56 @@ If this variable is nil, or if the provided function returns nil,
(number-sequence 1 (nnselect-artlist-length (number-sequence 1 (nnselect-artlist-length
gnus-newsgroup-selection)))) gnus-newsgroup-selection))))
(let* ((gnus-newsgroup-active nil) (let* ((gnus-newsgroup-active nil)
(artids (cl-sort nartids '< :key 'car)) (artids (cl-sort nartids #'< :key 'car))
(group-info (gnus-get-info artgroup)) (group-info (gnus-get-info artgroup))
(marks (gnus-info-marks group-info)) (marks (gnus-info-marks group-info))
(unread (gnus-uncompress-sequence (unread (gnus-uncompress-sequence
(gnus-range-difference (gnus-active artgroup) (gnus-range-difference (gnus-active artgroup)
(gnus-info-read group-info))))) (gnus-info-read group-info)))))
(gnus-atomic-progn
(setf (gnus-info-read info) (setf (gnus-info-read info)
(gnus-add-to-range (gnus-add-to-range
(gnus-info-read info) (gnus-info-read info)
(delq nil (delq nil (mapcar
(mapcar
#'(lambda (art) #'(lambda (art)
(unless (memq (cdr art) unread) (car art))) (unless (memq (cdr art) unread) (car art)))
artids)))) artids))))
(pcase-dolist (`(,type . ,range) marks) (pcase-dolist (`(,type . ,mark-list) marks)
(setq range (gnus-uncompress-sequence range)) (let ((mark-type (gnus-article-mark-to-type type)) new)
(gnus-add-marked-articles (when
group type (setq new
(delq nil (delq nil
(cond
((eq mark-type 'tuple)
(mapcar (mapcar
#'(lambda (art) #'(lambda (id)
(when (memq (cdr art) range) (let (mark)
(car art))) artids))))))) (when
(setq mark (assq (cdr id) mark-list))
(cons (car id) (cdr mark)))))
artids))
(t
(setq mark-list
(gnus-uncompress-range mark-list))
(mapcar
#'(lambda (id)
(when (memq (cdr id) mark-list)
(car id))) artids)))))
(let ((previous (alist-get type newmarks)))
(if previous
(nconc previous new)
(push (cons type new) newmarks))))))))
;; Clean up the marks: compress lists;
(pcase-dolist (`(,type . ,mark-list) newmarks)
(let ((mark-type (gnus-article-mark-to-type type)))
(unless (eq mark-type 'tuple)
(setf (alist-get type newmarks)
(gnus-compress-sequence mark-list)))))
;; and ensure an unexist key.
(unless (assq 'unexist newmarks)
(push (cons 'unexist nil) newmarks))
(gnus-info-set-marks info newmarks)
(gnus-set-active group (cons 1 (nnselect-artlist-length (gnus-set-active group (cons 1 (nnselect-artlist-length
gnus-newsgroup-selection))))) gnus-newsgroup-selection)))))
@ -769,42 +806,61 @@ article came from is also searched."
"Copy mark-lists from GROUP to the originating groups." "Copy mark-lists from GROUP to the originating groups."
(let ((select-unreads (numbers-by-group gnus-newsgroup-unreads)) (let ((select-unreads (numbers-by-group gnus-newsgroup-unreads))
(select-reads (numbers-by-group (select-reads (numbers-by-group
(gnus-uncompress-range (gnus-info-read (gnus-get-info group)) 'range))
(gnus-info-read (gnus-get-info group)))))
(select-unseen (numbers-by-group gnus-newsgroup-unseen)) (select-unseen (numbers-by-group gnus-newsgroup-unseen))
(gnus-newsgroup-active nil) (gnus-newsgroup-active nil) mark-list)
mark-list type-list) ;; collect the set of marked article lists categorized by
;; originating groups
(pcase-dolist (`(,mark . ,type) gnus-article-mark-lists) (pcase-dolist (`(,mark . ,type) gnus-article-mark-lists)
(let (type-list)
(when (setq type-list (when (setq type-list
(symbol-value (intern (format "gnus-newsgroup-%s" mark)))) (symbol-value (intern (format "gnus-newsgroup-%s" mark))))
(push (cons type (push (cons
(numbers-by-group type
(gnus-uncompress-range type-list))) mark-list))) (numbers-by-group type-list (gnus-article-mark-to-type type)))
mark-list))))
;; now work on each originating group one at a time
(pcase-dolist (`(,artgroup . ,artlist) (pcase-dolist (`(,artgroup . ,artlist)
(numbers-by-group gnus-newsgroup-articles)) (numbers-by-group gnus-newsgroup-articles))
(let* ((group-info (gnus-get-info artgroup)) (let* ((group-info (gnus-get-info artgroup))
(old-unread (gnus-list-of-unread-articles artgroup)) (old-unread (gnus-list-of-unread-articles artgroup))
newmarked) newmarked delta-marks)
(when group-info (when group-info
;; iterate over mark lists for this group
(pcase-dolist (`(,_mark . ,type) gnus-article-mark-lists) (pcase-dolist (`(,_mark . ,type) gnus-article-mark-lists)
(let ((select-type (let ((list (cdr (assoc artgroup (alist-get type mark-list))))
(sort (mark-type (gnus-article-mark-to-type type)))
(cdr (assoc artgroup (alist-get type mark-list)))
'<)) list)
(setq list
(gnus-uncompress-range
(gnus-add-to-range
(gnus-remove-from-range
(alist-get type (gnus-info-marks group-info))
artlist)
select-type)))
(when list ;; When the backend can store marks we collect any
;; Get rid of the entries of the articles that have the ;; changes. Unlike a normal group the mark lists only
;; default score. ;; include marks for articles we retrieved.
(when (and (eq type 'score) (when (and (gnus-check-backend-function
gnus-save-score 'request-set-mark artgroup)
list) (not (gnus-article-unpropagatable-p type)))
(let* ((old (gnus-list-range-intersection
artlist
(alist-get type (gnus-info-marks group-info))))
(del (gnus-remove-from-range (copy-tree old) list))
(add (gnus-remove-from-range (copy-tree list) old)))
(when add (push (list add 'add (list type)) delta-marks))
(when del
;; Don't delete marks from outside the active range.
;; This shouldn't happen, but is a sanity check.
(setq del (gnus-sorted-range-intersection
(gnus-active artgroup) del))
(push (list del 'del (list type)) delta-marks))))
;; Marked sets are of mark-type 'tuple, 'list, or
;; 'range. We merge the lists with what is already in
;; the original info to get full list of new marks. We
;; do this by removing all the articles we retrieved
;; from the full list, and then add back in the newly
;; marked ones.
(cond
((eq mark-type 'tuple)
;; Get rid of the entries that have the default
;; score.
(when (and list (eq type 'score) gnus-save-score)
(let* ((arts list) (let* ((arts list)
(prev (cons nil list)) (prev (cons nil list))
(all prev)) (all prev))
@ -814,30 +870,41 @@ article came from is also searched."
(setcdr prev (cdr arts)) (setcdr prev (cdr arts))
(setq prev arts)) (setq prev arts))
(setq arts (cdr arts))) (setq arts (cdr arts)))
(setq list (cdr all))))) (setq list (cdr all))))
;; now merge with the original list and sort just to
(when (or (eq (gnus-article-mark-to-type type) 'list) ;; make sure
(eq (gnus-article-mark-to-type type) 'range))
(setq list (setq list
(gnus-compress-sequence (sort list '<) t))) (sort (map-merge
'list list
(alist-get type (gnus-info-marks group-info)))
(lambda (elt1 elt2)
(< (car elt1) (car elt2))))))
(t
(setq list
(gnus-compress-sequence
(gnus-sorted-union
(gnus-sorted-difference
(gnus-uncompress-sequence
(alist-get type (gnus-info-marks group-info)))
artlist)
(sort list #'<)) t)))
;; When exiting the group, everything that's previously been ;; When exiting the group, everything that's previously been
;; unseen is now seen. ;; unseen is now seen.
(when (eq type 'seen) (when (eq type 'seen)
(setq list (gnus-range-add (setq list (gnus-range-add
list (cdr (assoc artgroup select-unseen))))) list (cdr (assoc artgroup select-unseen))))))
(when (or list (eq type 'unexist)) (when (or list (eq type 'unexist))
(push (cons type list) newmarked)))) (push (cons type list) newmarked)))) ;; end of mark-type loop
(when delta-marks
(unless (gnus-check-group artgroup)
(error "Can't open server for %s" artgroup))
(gnus-request-set-mark artgroup delta-marks))
(gnus-atomic-progn (gnus-atomic-progn
;; Enter these new marks into the info of the group. (gnus-info-set-marks group-info newmarked)
(if (nthcdr 3 group-info)
(setcar (nthcdr 3 group-info) newmarked)
;; Add the marks lists to the end of the info.
(when newmarked
(setcdr (nthcdr 2 group-info) (list newmarked))))
;; Cut off the end of the info if there's nothing else there. ;; Cut off the end of the info if there's nothing else there.
(let ((i 5)) (let ((i 5))
(while (and (> i 2) (while (and (> i 2)