summaryrefslogtreecommitdiff
path: root/lisp/gnus/gnus-sum.el
diff options
context:
space:
mode:
authorEric Abrahamsen <eric@ericabrahamsen.net>2018-04-26 16:26:27 -0700
committerEric Abrahamsen <eric@ericabrahamsen.net>2019-03-22 10:23:30 -0700
commitc1b63af4458e92bad33da0def2b15c206656e2fa (patch)
tree267503989ec0475b76800bb309f6cdc1da53e74e /lisp/gnus/gnus-sum.el
parent3375d08299bbc1e224d19a871012cdbbf5d787ee (diff)
downloademacs-c1b63af4458e92bad33da0def2b15c206656e2fa.tar.gz
emacs-c1b63af4458e92bad33da0def2b15c206656e2fa.tar.bz2
emacs-c1b63af4458e92bad33da0def2b15c206656e2fa.zip
Change Gnus hash tables into real hash tables
Gnus has used obarrays as makeshift hash tables for groups: group names are coerced to unibyte and interned in custom obarrays, and their symbol-value set to whatever value needs to be stored. This patch replaces those obarrays with actual hash tables. * lisp/gnus/gnus-util.el (gnus-intern-safe, gnus-create-hash-size): Remove functions. (gnus-make-hashtable): Change to return a real hash table. (gnus-text-property-search): Utility similar to `text-property-any', but compares on `equal'. Needed because the 'gnus-group text property is now a string. * lisp/gnus/gnus.el (gnus-gethash, gnus-gethash-safe, gnus-sethash): Remove macros. (gnus-group-list): New variable holding all group names as an ordered list. Used because `gnus-newsrc-hashtb' used to preserve `gnus-newsrc-alist' ordering, but now doesn't. * lisp/gnus/nnmaildir.el (nnmaildir--servers): Change from obarray to alist. (nnmaildir--up2-1): Remove function. * lisp/thingatpt.el (thing-at-point-newsgroup-p): This was making use of Gnus obarrays, replace with a cond that can handle many different possibilities. * lisp/gnus/gnus-bcklg.el (gnus-backlog-articles): Remove gnus-backlog-hashtb, which wasn't doing anything. Just keep a list of ident strings in gnus-backlog-articles. (gnus-backlog-setup): Delete unnecessary function. (gnus-backlog-enter-article, gnus-backlog-remove-oldest-article, gnus-backlog-remove-article, gnus-backlog-request-article): Alter calls accordingly. * lisp/gnus/gnus-dup.el (gnus-duplicate-list-max-length): Rename from `gnus-duplicate-list-length', for accuracy. * lisp/gnus/gnus-start.el (gnus-active-to-gnus-format, gnus-groups-to-gnus-format, gnus-newsrc-to-gnus-format): Read group names as strings. (gnus-gnus-to-quick-newsrc-format): Write `gnus-newsrc-alist' using the ordering in `gnus-group-list'. * lisp/gnus/gnus-agent.el: * lisp/gnus/gnus-async.el: * lisp/gnus/gnus-cache.el: * lisp/gnus/gnus-group.el: * lisp/gnus/gnus-score.el: * lisp/gnus/gnus-sum.el: * lisp/gnus/gnus-topic.el: * lisp/gnus/message.el: * lisp/gnus/mml.el: * lisp/gnus/nnagent.el: * lisp/gnus/nnbabyl.el: * lisp/gnus/nnvirtual.el: * lisp/gnus/nnweb.el: In all files, change obarrays to hash-tables, and swap `gnus-sethash' for `puthash', `gnus-gethash' for `gethash', `mapatoms' for `maphash', etc. * test/lisp/gnus/gnus-test-headers.el (gnus-headers-make-dependency-table, gnus-headers-loop-dependencies): New tests to make sure we're building `gnus-newsgroup-dependencies' correctly.
Diffstat (limited to 'lisp/gnus/gnus-sum.el')
-rw-r--r--lisp/gnus/gnus-sum.el220
1 files changed, 116 insertions, 104 deletions
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index efb3e4f1a66..85c902a5e43 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -39,6 +39,8 @@
(require 'gmm-utils)
(require 'mm-decode)
(require 'nnoo)
+(eval-when-compile
+ (require 'subr-x))
(autoload 'gnus-summary-limit-include-cached "gnus-cache" nil t)
(autoload 'gnus-cache-write-active "gnus-cache")
@@ -1361,7 +1363,15 @@ the normal Gnus MIME machinery."
(defvar gnus-current-crosspost-group nil)
(defvar gnus-newsgroup-display nil)
-(defvar gnus-newsgroup-dependencies nil)
+(defvar gnus-newsgroup-dependencies nil
+ "A hash table holding dependencies between messages.")
+;; Dependencies are held in a tree structure: a list with the root
+;; message as car, and each immediate child a sublist (perhaps
+;; containing further sublists). Each message is represented as a
+;; vector of headers. Each message's list can be looked up in the
+;; dependency table using the message's Message-ID as the key. The
+;; root key is the string "none".
+
(defvar gnus-newsgroup-adaptive nil)
(defvar gnus-summary-display-article-function nil)
(defvar gnus-summary-highlight-line-function nil
@@ -3937,7 +3947,7 @@ If SELECT-ARTICLES, only select those articles from GROUP."
;; Killed foreign groups can't be entered.
;; (when (and (not (gnus-group-native-p group))
- ;; (not (gnus-gethash group gnus-newsrc-hashtb)))
+ ;; (not (gethash group gnus-newsrc-hashtb)))
;; (error "Dead non-native groups can't be entered"))
(gnus-message 7 "Retrieving newsgroup: %s..."
(gnus-group-decoded-name group))
@@ -4167,7 +4177,7 @@ If SELECT-ARTICLES, only select those articles from GROUP."
"Gather threads by looking at Subject headers."
(if (not gnus-summary-make-false-root)
threads
- (let ((hashtb (gnus-make-hashtable 1024))
+ (let ((hashtb (gnus-make-hashtable 1000))
(prev threads)
(result threads)
subject hthread whole-subject)
@@ -4176,7 +4186,7 @@ If SELECT-ARTICLES, only select those articles from GROUP."
(setq whole-subject (mail-header-subject
(caar threads)))))
(when subject
- (if (setq hthread (gnus-gethash subject hashtb))
+ (if (setq hthread (gethash subject hashtb))
(progn
;; We enter a dummy root into the thread, if we
;; haven't done that already.
@@ -4190,24 +4200,24 @@ If SELECT-ARTICLES, only select those articles from GROUP."
(setcdr prev (cdr threads))
(setq threads prev))
;; Enter this thread into the hash table.
- (gnus-sethash subject
- (if gnus-summary-make-false-root-always
- (progn
- ;; If you want a dummy root above all
- ;; threads...
- (setcar threads (list whole-subject
- (car threads)))
- threads)
- threads)
- hashtb)))
+ (puthash subject
+ (if gnus-summary-make-false-root-always
+ (progn
+ ;; If you want a dummy root above all
+ ;; threads...
+ (setcar threads (list whole-subject
+ (car threads)))
+ threads)
+ threads)
+ hashtb)))
(setq prev threads)
(setq threads (cdr threads)))
result)))
(defun gnus-gather-threads-by-references (threads)
"Gather threads by looking at References headers."
- (let ((idhashtb (gnus-make-hashtable 1024))
- (thhashtb (gnus-make-hashtable 1024))
+ (let ((idhashtb (gnus-make-hashtable 1000))
+ (thhashtb (gnus-make-hashtable 1000))
(prev threads)
(result threads)
ids references id gthread gid entered ref)
@@ -4218,11 +4228,11 @@ If SELECT-ARTICLES, only select those articles from GROUP."
entered nil)
(while (setq ref (pop ids))
(setq ids (delete ref ids))
- (if (not (setq gid (gnus-gethash ref idhashtb)))
+ (if (not (setq gid (gethash ref idhashtb)))
(progn
- (gnus-sethash ref id idhashtb)
- (gnus-sethash id threads thhashtb))
- (setq gthread (gnus-gethash gid thhashtb))
+ (puthash ref id idhashtb)
+ (puthash id threads thhashtb))
+ (setq gthread (gethash gid thhashtb))
(unless entered
;; We enter a dummy root into the thread, if we
;; haven't done that already.
@@ -4234,7 +4244,7 @@ If SELECT-ARTICLES, only select those articles from GROUP."
(setcdr (car gthread)
(nconc (cdar gthread) (list (car threads)))))
;; Add it into the thread hash table.
- (gnus-sethash id gthread thhashtb)
+ (puthash id gthread thhashtb)
(setq entered t)
;; Remove it from the list of threads.
(setcdr prev (cdr threads))
@@ -4267,12 +4277,12 @@ If SELECT-ARTICLES, only select those articles from GROUP."
;; We have found a loop.
(let (ref-dep)
(setcdr thread (delq (car th) (cdr thread)))
- (if (boundp (setq ref-dep (intern "none"
- gnus-newsgroup-dependencies)))
- (setcdr (symbol-value ref-dep)
- (nconc (cdr (symbol-value ref-dep))
+ (if (setq ref-dep (gethash "none"
+ gnus-newsgroup-dependencies))
+ (setcdr ref-dep
+ (nconc (cdr ref-dep)
(list (car th))))
- (set ref-dep (list nil (car th))))
+ (puthash ref-dep (list nil (car th)) gnus-newsgroup-dependencies))
(setq infloop 1
stack nil))
;; Push all the subthreads onto the stack.
@@ -4283,31 +4293,30 @@ If SELECT-ARTICLES, only select those articles from GROUP."
"Go through the dependency hashtb and find the roots. Return all threads."
(let (threads)
(while (catch 'infloop
- (mapatoms
- (lambda (refs)
+ (maphash
+ (lambda (_id refs)
;; Deal with self-referencing References loops.
- (when (and (car (symbol-value refs))
+ (when (and (car refs)
(not (zerop
(apply
'+
(mapcar
(lambda (thread)
(gnus-thread-loop-p
- (car (symbol-value refs)) thread))
- (cdr (symbol-value refs)))))))
+ (car refs) thread))
+ (cdr refs))))))
(setq threads nil)
(throw 'infloop t))
- (unless (car (symbol-value refs))
+ (unless (car refs)
;; These threads do not refer back to any other
;; articles, so they're roots.
- (setq threads (append (cdr (symbol-value refs)) threads))))
+ (setq threads (append (cdr refs) threads))))
gnus-newsgroup-dependencies)))
threads))
;; Build the thread tree.
(defsubst gnus-dependencies-add-header (header dependencies force-new)
"Enter HEADER into the DEPENDENCIES table if it is not already there.
-
If FORCE-NEW is not nil, enter HEADER into the DEPENDENCIES table even
if it was already present.
@@ -4318,33 +4327,38 @@ Message-ID before being entered.
Returns HEADER if it was entered in the DEPENDENCIES. Returns nil otherwise."
(let* ((id (mail-header-id header))
- (id-dep (and id (intern id dependencies)))
+ ;; An "id-dep" is a list holding the vector headers of this
+ ;; message, plus equivalent "id-deps" for each immediate
+ ;; child message.
+ (id-dep (and id (gethash id dependencies)))
parent-id ref ref-dep ref-header replaced)
;; Enter this `header' in the `dependencies' table.
(cond
- ((not id-dep)
+ ((null id)
+ ;; Omit this article altogether if there is no Message-ID.
(setq header nil))
- ;; The first two cases do the normal part: enter a new `header'
- ;; in the `dependencies' table.
- ((not (boundp id-dep))
- (set id-dep (list header)))
- ((null (car (symbol-value id-dep)))
- (setcar (symbol-value id-dep) header))
-
+ ;; Enter a new id and `header' in the `dependencies' table.
+ ((null id-dep)
+ (setq id-dep (puthash id (list header) dependencies)))
+ ;; A child message has already added this id, just insert the header.
+ ((null (car id-dep))
+ (setcar (gethash id dependencies) header)
+ (setq id-dep (gethash id dependencies)))
;; From here the `header' was already present in the
;; `dependencies' table.
(force-new
;; Overrides an existing entry;
;; just set the header part of the entry.
- (setcar (symbol-value id-dep) header)
+ (setcar (gethash id dependencies) header)
(setq replaced t))
;; Renames the existing `header' to a unique Message-ID.
((not gnus-summary-ignore-duplicates)
;; An article with this Message-ID has already been seen.
;; We rename the Message-ID.
- (set (setq id-dep (intern (setq id (nnmail-message-id)) dependencies))
- (list header))
+ (setq id-dep (puthash (setq id (nnmail-message-id))
+ (list header)
+ dependencies))
(mail-header-set-id header id))
;; The last case ignores an existing entry, except it adds any
@@ -4354,8 +4368,8 @@ Returns HEADER if it was entered in the DEPENDENCIES. Returns nil otherwise."
;; table was *not* modified.
(t
(mail-header-set-xref
- (car (symbol-value id-dep))
- (concat (or (mail-header-xref (car (symbol-value id-dep)))
+ (car id-dep)
+ (concat (or (mail-header-xref (car id-dep))
"")
(or (mail-header-xref header) "")))
(setq header nil)))
@@ -4365,23 +4379,27 @@ Returns HEADER if it was entered in the DEPENDENCIES. Returns nil otherwise."
(setq parent-id (gnus-parent-id (mail-header-references header)))
(setq ref parent-id)
(while (and ref
- (setq ref-dep (intern-soft ref dependencies))
- (boundp ref-dep)
- (setq ref-header (car (symbol-value ref-dep))))
+ (setq ref-dep (gethash ref dependencies))
+ (setq ref-header (car-safe ref-dep)))
(if (string= id ref)
;; Yuk! This is a reference loop. Make the article be a
;; root article.
(progn
- (mail-header-set-references (car (symbol-value id-dep)) "none")
+ (mail-header-set-references (car id-dep) "none")
(setq ref nil)
(setq parent-id nil))
(setq ref (gnus-parent-id (mail-header-references ref-header)))))
- (setq ref-dep (intern (or parent-id "none") dependencies))
- (if (boundp ref-dep)
- (setcdr (symbol-value ref-dep)
- (nconc (cdr (symbol-value ref-dep))
- (list (symbol-value id-dep))))
- (set ref-dep (list nil (symbol-value id-dep)))))
+ (setq ref (or parent-id "none")
+ ref-dep (gethash ref dependencies))
+ ;; Add `header' to its parent's list of children, creating that
+ ;; list if the parent isn't yet registered in the dependency
+ ;; table.
+ (if ref-dep
+ (setcdr (gethash ref dependencies)
+ (nconc (cdr ref-dep)
+ (list id-dep)))
+ (puthash ref (list nil id-dep)
+ dependencies)))
header))
(defun gnus-extract-message-id-from-in-reply-to (string)
@@ -4444,15 +4462,14 @@ Returns HEADER if it was entered in the DEPENDENCIES. Returns nil otherwise."
;; server, that is.
(let ((mail-parse-charset gnus-newsgroup-charset)
id heads)
- (mapatoms
- (lambda (refs)
- (when (not (car (symbol-value refs)))
- (setq heads (cdr (symbol-value refs)))
+ (maphash
+ (lambda (id refs)
+ (when (not (car refs))
+ (setq heads (cdr refs))
(while heads
(if (memq (mail-header-number (caar heads))
gnus-newsgroup-dormant)
(setq heads (cdr heads))
- (setq id (symbol-name refs))
(while (and (setq id (gnus-build-get-header id))
(not (car (gnus-id-to-thread id)))))
(setq heads nil)))))
@@ -4733,7 +4750,7 @@ If LINE, insert the rebuilt thread starting on line LINE."
(defun gnus-id-to-thread (id)
"Return the (sub-)thread where ID appears."
- (gnus-gethash id gnus-newsgroup-dependencies))
+ (gethash id gnus-newsgroup-dependencies))
(defun gnus-id-to-article (id)
"Return the article number of ID."
@@ -5586,7 +5603,7 @@ If SELECT-ARTICLES, only select those articles from GROUP."
(if (eq (car (gnus-find-method-for-group group)) 'nnvirtual)
t
gnus-summary-ignore-duplicates))
- (info (nth 2 entry))
+ (info (nth 1 entry))
charset articles fetched-articles cached)
(unless (gnus-check-server
@@ -5605,7 +5622,7 @@ If SELECT-ARTICLES, only select those articles from GROUP."
(decode-coding-string group charset)
(decode-coding-string (gnus-status-message group) charset))))
- (unless (gnus-request-group group t nil (gnus-get-info group))
+ (unless (gnus-request-group group t nil info)
(when (derived-mode-p 'gnus-summary-mode)
(gnus-kill-buffer (current-buffer)))
(error "Couldn't request group %s: %s"
@@ -6208,9 +6225,9 @@ The resulting hash table is returned, or nil if no Xrefs were found."
(setq number
(string-to-number (substring xrefs (match-beginning 2)
(match-end 2))))
- (if (setq entry (gnus-gethash group xref-hashtb))
+ (if (setq entry (gethash group xref-hashtb))
(setcdr entry (cons number (cdr entry)))
- (gnus-sethash group (cons number nil) xref-hashtb)))))
+ (puthash group (cons number nil) xref-hashtb)))))
(and start xref-hashtb)))
(defun gnus-mark-xrefs-as-read (from-newsgroup headers unreads)
@@ -6220,10 +6237,9 @@ The resulting hash table is returned, or nil if no Xrefs were found."
(with-current-buffer gnus-group-buffer
(when (setq xref-hashtb
(gnus-create-xref-hashtb from-newsgroup headers unreads))
- (mapatoms
- (lambda (group)
- (unless (string= from-newsgroup (setq name (symbol-name group)))
- (setq idlist (symbol-value group))
+ (maphash
+ (lambda (group idlist)
+ (unless (string= from-newsgroup group)
;; Dead groups are not updated.
(and (prog1
(setq info (gnus-get-info name))
@@ -6249,7 +6265,7 @@ The resulting hash table is returned, or nil if no Xrefs were found."
(defun gnus-compute-read-articles (group articles)
(let* ((entry (gnus-group-entry group))
- (info (nth 2 entry))
+ (info (nth 1 entry))
(active (gnus-active group))
ninfo)
(when entry
@@ -6286,7 +6302,7 @@ The resulting hash table is returned, or nil if no Xrefs were found."
"Update the info of GROUP to say that ARTICLES are read."
(let* ((num 0)
(entry (gnus-group-entry group))
- (info (nth 2 entry))
+ (info (nth 1 entry))
(active (gnus-active group))
(set-marks
(gnus-method-option-p
@@ -8848,11 +8864,11 @@ fetch-old-headers verbiage, and so on."
(null gnus-thread-expunge-below)))
(push gnus-newsgroup-limit gnus-newsgroup-limits)
(setq gnus-newsgroup-limit nil)
- (mapatoms
- (lambda (node)
- (unless (car (symbol-value node))
+ (maphash
+ (lambda (id deps)
+ (unless (car deps)
;; These threads have no parents -- they are roots.
- (let ((nodes (cdr (symbol-value node)))
+ (let ((nodes (cdr deps))
thread)
(while nodes
(if (and gnus-thread-expunge-below
@@ -12288,12 +12304,11 @@ save those articles instead."
(nreverse split-name)))
(defun gnus-valid-move-group-p (group)
- (and (symbolp group)
- (boundp group)
- (symbol-name group)
- (symbol-value group)
- (gnus-get-function (gnus-find-method-for-group
- (symbol-name group)) 'request-accept-article t)))
+ (when (and (stringp group)
+ (null (string-empty-p group)))
+ (gnus-get-function (gnus-find-method-for-group
+ group)
+ 'request-accept-article t)))
(defun gnus-read-move-group-name (prompt default articles prefix)
"Read a group name."
@@ -12304,27 +12319,24 @@ save those articles instead."
(if (> (length articles) 1)
(format "these %d articles" (length articles))
"this article")))
- valid-names
+ (valid-names
+ (seq-filter #'gnus-valid-move-group-p
+ (hash-table-keys gnus-active-hashtb)))
(to-newsgroup
- (progn
- (mapatoms (lambda (g)
- (when (gnus-valid-move-group-p g)
- (push g valid-names)))
- gnus-active-hashtb)
- (cond
- ((null split-name)
- (gnus-group-completing-read
- prom
- valid-names
- nil prefix nil default))
- ((= 1 (length split-name))
- (gnus-group-completing-read
- prom
- valid-names
- nil prefix 'gnus-group-history (car split-name)))
- (t
- (gnus-completing-read
- prom (nreverse split-name) nil nil 'gnus-group-history)))))
+ (cond
+ ((null split-name)
+ (gnus-group-completing-read
+ prom
+ valid-names
+ nil prefix nil default))
+ ((= 1 (length split-name))
+ (gnus-group-completing-read
+ prom
+ valid-names
+ nil prefix 'gnus-group-history (car split-name)))
+ (t
+ (gnus-completing-read
+ prom (nreverse split-name) nil nil 'gnus-group-history))))
(to-method (gnus-server-to-method (gnus-group-method to-newsgroup)))
encoded)
(when to-newsgroup