>From 54243028a84a4c1b915d45fea7540638b1c26c95 Mon Sep 17 00:00:00 2001 From: dickmao Date: Wed, 16 Jun 2021 13:50:40 -0400 Subject: [PATCH] Major changes to Gnus Obsolesce gnus-secondary-select-methods for gnus-select-methods. Rationalize newsrc logic. Rationalize nnimap. Obviate `gnus-set-global-variables`, `gnus-summary-local-variables`. Toggle `gnus-background-get-unread-articles` parallel fetch thread. * doc/emacs/misc.texi (Gnus Startup): Declutter. * doc/misc/auth.texi (Multiple GMail accounts with Gnus): Declutter. * doc/misc/gnus.texi (Finding the News): Declutter. (Startup Files): Declutter. (Startup Variables): Declutter. (Foreign Groups): Declutter. (More Threading): Declutter. (Little Disk Space): Declutter. * etc/gnus/news-server.ast (User name and password): Whitespace. * lisp/gnus/gnus-agent.el (gnus): Declutter. (gnus-cache): Declutter. (nnvirtual): Declutter. (gnus-sum): Declutter. (gnus-score): Declutter. (gnus-srvr): Declutter. (gnus-util): Declutter. (require): Declutter. (gnus-start): Declutter. (gnus-all-score-files): Declutter. (gnus-server-set-status): Declutter. (gnus-server-named-server): Declutter. (gnus-inews-do-gcc): Declutter. (gnus-inews-group-method): Declutter. (gnus-cache-coding-system): Declutter. (gnus-agent-read-agentview): Declutter. * lisp/gnus/gnus-art.el (require): Declutter. (seq): Declutter. (gnus-article-wash-types): Buffer-localize. (gnus-article-image-alist): Buffer-localize. (gnus-original-group-and-article): Buffer-localize. (gnus-article-mime-handle-alist): Buffer-localize. (gnus-article-current-summary): Buffer-localize. (gnus-article-edit-mode): Declutter. (gnus-article-mode): Declutter. (gnus-article-setup-buffer-ensure): Refactor. (gnus-article-setup-buffer): Refactor. (gnus-article-prepare): gnus-summary-assume-in-summary, not set-buffer. (gnus-article-prepare-display): Replace nonsense hook. (gnus-article-show-summary): Delete gnus-article-set-globals. (gnus-article-followup-with-original): Whitespace. (gnus-request-article-this-buffer): Rationalize. (gnus-flush-original-article-buffer): Consolidate state. (gnus-article-set-globals): Delete gnus-article-set-globals. (gnus-article-encrypt-body): Delete gnus-set-global-variables. * lisp/gnus/gnus-async.el (require): Declutter. * lisp/gnus/gnus-cache.el (gnus): Declutter. (gnus-cache-removable-articles): Buffer-localize. (gnus-cache-read-active): gnus-with-temp-buffer, not gnus-set-work-buffer. * lisp/gnus/gnus-demon.el (require): Declutter. * lisp/gnus/gnus-draft.el (gnus): Declutter. (message): Declutter. (gnus-cache): Declutter. * lisp/gnus/gnus-fun.el (gnus-util): Declutter. * lisp/gnus/gnus-group.el (mm-url): Declutter. (subr-x): Declutter. (require): Declutter. (gnus-group-mode): Elide gnus-update-format-specifications bogus argument. (gnus-update-group-mark-positions): gnus-with-temp-buffer, not gnus-set-work-buffer. (gnus-group-list-groups): Elide gnus-update-format-specifications bogus argument. (gnus-group-insert-group-line): Declutter. (gnus-group-update-eval-form): Declutter. (gnus-group-first-unread-group): Declutter. (gnus-group-list-active): Declutter. (gnus-group-quit): Delete gnus-current-startup-file. (gnus-group-set-info): Declutter. * lisp/gnus/gnus-icalendar.el (require): Declutter. * lisp/gnus/gnus-int.el (gnus-agent-unfetch-articles): Declutter. (gnus-summary-select-article): Declutter. (gnus-summary-insert-subject): Declutter. (gnus-summary-setup-buffer): Declutter. (gnus-summary-read-group-1): Declutter. (gnus-server-opened): Declutter. (gnus-start-news-server): Obsolesce gnus-secondary-select-methods. (gnus-get-function): Declutter. (gnus-request-group): Declutter. (gnus-close-group): Declutter. * lisp/gnus/gnus-kill.el (gnus-apply-kill-file-internal): prog1 for clarity. * lisp/gnus/gnus-logic.el (require): Declutter. (gnus-util): Declutter. * lisp/gnus/gnus-mlspl.el (gnus-group-split-setup): Rationalize newsrc logic. * lisp/gnus/gnus-msg.el (require): Declutter. (gnus): Declutter. (gnus-util): Declutter. (cl-extra): Declutter. (gnus-setup-message): Declutter. (gnus-inews-add-send-actions): Buffer-localize. (gnus-group-post-news): gnus-article-copy is better as a variable. (gnus-copy-article-buffer): Use with-current-buffer instead of set-buffer. (gnus-msg-preserve-variables): Buffer-local variables need to propagate. (gnus-msg-inherit-variables): Buffer-local variables need to propagate. (gnus-post-news): Buffer-local variables need to propagate. (gnus-post-method): Obsolesce gnus-secondary-select-methods. (gnus-summary-reply): Whitespace. * lisp/gnus/gnus-notifications.el (gnus-util): Declutter. * lisp/gnus/gnus-picon.el (require): Declutter. * lisp/gnus/gnus-registry.el (gnus-util): Declutter. (subr-x): Declutter. (gnus-registry-initialize): Rationalize newsrc logic. (gnus-registry-unload-hook): Rationalize newsrc logic. * lisp/gnus/gnus-salt.el (require): Declutter. (gnus-pick-mode): Elide gnus-update-format-specifications bogus argument. (gnus-tree-mode): gnus-with-temp-buffer, not gnus-set-work-buffer. * lisp/gnus/gnus-score.el (gnus-score-alist): Buffer-localize. (gnus-score-save): Declutter. (gnus-score-headers): Whitespace. (gnus-sort-score-files): Declutter. * lisp/gnus/gnus-search.el (gnus-util): Declutter. (require): Declutter. (gnus-search-run-search): nnimap-process-buffer, not nnimap-buffer. * lisp/gnus/gnus-spec.el (require): Declutter. (gnus-newsrc-file-version): Buffer-localize. (gnus-summary-mark-positions): Buffer-localize. (gnus-update-format-specifications): Elide bogus argument. (gnus-parse-format): Spaces, not tabs. (gnus-parse-complex-format): gnus-with-temp-buffer, not gnus-set-work-buffer. (gnus-parse-simple-format): gnus-with-temp-buffer, not gnus-set-work-buffer. * lisp/gnus/gnus-srvr.el (require): Declutter. (gnus-browse-foreign-server): Obsolesce gnus-secondary-select-methods. * lisp/gnus/gnus-start.el (gnus-util): Declutter. (nnmail): Declutter. (gnus-group-decoded-name): Declutter. (gnus-agent-save-active): Declutter. (gnus-agentize): Declutter. (require): Declutter. (gnus-group-default-level): Declutter. (gnus-inhibit-demon): Declutter. (gnus-startup-file): Rationalize newsrc logic. (gnus-thread-group): Prefix for gnus fetch thread group. (gnus-backup-startup-file): Rationalize newsrc logic. (gnus-max-seconds-hold-mutex): Self-explanatory. (gnus-save-startup-file-via-temp-buffer): Rationalize newsrc logic. (gnus-mutex-get-unread-articles): Self-explanatory. (gnus-dot-newsrc): What used to be gnus-startup-file. (gnus-newsrc-file): What used to be gnus-newsrc-el-file. (gnus-check-new-newsgroups): Obsolesce gnus-secondary-select-methods. (gnus-read-newsrc-file): Rationalize newsrc logic. (gnus-save-newsrc-file): Rationalize newsrc logic. (gnus-save-dot-newsrc): Rationalize newsrc logic. (gnus-background-get-unread-articles): Enable backgrounding fetch. (gnus-read-newsrc-el-hook): Rationalize newsrc logic. (gnus-read-newsrc-hook): Rationalize newsrc logic. (gnus-save-newsrc-hook): Rationalize newsrc logic. (gnus-save-quick-newsrc-hook): Rationalize newsrc logic. (gnus-save-standard-newsrc-hook): Rationalize newsrc logic. (gnus-current-startup-file): Rationalize newsrc logic. (gnus-subscribe-hierarchically): Rationalize newsrc logic. (gnus-clear-system): Use when instead of and for conditionals. (gnus-1): Whitespace. (gnus-dribble-file-name): Rationalize newsrc logic. (gnus-dribble-read-file): Rationalize newsrc logic. (gnus-setup-news): Call out a bogus argument. (gnus-ask-server-for-new-groups): Obsolesce gnus-secondary-select-methods. (gnus-activate-group): Whitespace. (gnus-get-unread-articles-in-group): Declutter. (gnus-scope-globals): Buffer-localize. (gnus-thread-body): Enable backgrounding fetch. (gnus-thread-group-running-p): Enable backgrounding fetch. (gnus-run-thread): Enable backgrounding fetch. (gnus-chain-arg): Enable backgrounding fetch. (gnus-time-out-thread): Enable backgrounding fetch. (gnus-get-unread-articles): Enable backgrounding fetch. (gnus-method-rank): This became superfluous over the decades. (gnus-read-active-for-groups): Spaces, not tabs. (gnus-read-active-file): Obsolesce gnus-secondary-select-methods. (gnus-read-active-file-1): Declutter. (gnus-clean-old-newsrc): Rationalize newsrc logic. (gnus-read-newsrc-el-file): Rationalize newsrc logic. (gnus-make-newsrc-file): Rationalize newsrc logic. (gnus-newsrc-to-gnus-format): Declutter. (gnus-newsrc-parse-options): gnus-with-temp-buffer, not gnus-set-work-buffer. (defalias): Declutter. (gnus-gnus-to-quick-newsrc-format): Rationalize newsrc logic. (gnus-gnus-to-newsrc-format): Rationalize newsrc logic. (gnus-child-save-newsrc): Rationalize newsrc logic. (gnus-parent-read-child-newsrc): Rationalize newsrc logic. (gnus-read-all-descriptions-files): Obsolesce gnus-secondary-select-methods. * lisp/gnus/gnus-sum.el (gnus-util): Declutter. (require): Declutter. (subr-x): Declutter. (gnus-agent-get-undownloaded-list): Declutter. (gnus-summary-mark-below): Buffer-localize. (gnus-summary-expunge-below): Buffer-localize. (gnus-thread-expunge-below): Buffer-localize. (gnus-orphan-score): Buffer-localize. (gnus-article-mime-handles): Buffer-localize. (gnus-article-decoded-p): Buffer-localize. (gnus-article-charset): Buffer-localize. (gnus-scores-exclude-files): Buffer-localize. (gnus-page-broken): Buffer-localize. (gnus-original-article): Delete. (gnus-newsgroup-process-stack): Buffer-localize. (gnus-sort-gathered-threads-function): Declutter. (gnus-newsgroup-selected-overlay): Buffer-localize. (gnus-newsgroup-adaptive-score-file): Buffer-localize. (gnus-current-score-file): Buffer-localize. (gnus-newsgroup-display): Buffer-localize. (gnus-newsgroup-dependencies): Buffer-localize. (gnus-newsgroup-adaptive): Buffer-localize. (gnus-summary-highlight-line-function): Buffer-localize. (gnus-newsgroup-begin): Buffer-localize. (gnus-newsgroup-end): Buffer-localize. (gnus-newsgroup-last-rmail): Buffer-localize. (gnus-newsgroup-last-mail): Buffer-localize. (gnus-newsgroup-last-folder): Buffer-localize. (gnus-newsgroup-last-file): Buffer-localize. (gnus-newsgroup-last-directory): Buffer-localize. (gnus-newsgroup-auto-expire): Buffer-localize. (gnus-newsgroup-active): Buffer-localize. (gnus-newsgroup-highest): Buffer-localize. (gnus-newsgroup-data): Buffer-localize. (gnus-newsgroup-data-reverse): Buffer-localize. (gnus-newsgroup-limit): Buffer-localize. (gnus-newsgroup-limits): Buffer-localize. (gnus-summary-use-undownloaded-faces): Buffer-localize. (gnus-newsgroup-unreads): Buffer-localize. (gnus-newsgroup-unselected): Buffer-localize. (gnus-newsgroup-reads): Buffer-localize. (gnus-newsgroup-expunged-tally): Buffer-localize. (gnus-newsgroup-marked): Buffer-localize. (gnus-newsgroup-spam-marked): Buffer-localize. (gnus-newsgroup-killed): Buffer-localize. (gnus-newsgroup-cached): Buffer-localize. (gnus-newsgroup-saved): Buffer-localize. (gnus-newsgroup-kill-headers): Buffer-localize. (gnus-newsgroup-replied): Buffer-localize. (gnus-newsgroup-forwarded): Buffer-localize. (gnus-newsgroup-expirable): Buffer-localize. (gnus-newsgroup-processable): Buffer-localize. (gnus-newsgroup-downloadable): Buffer-localize. (gnus-newsgroup-unfetched): Buffer-localize. (gnus-newsgroup-undownloaded): Buffer-localize. (gnus-newsgroup-unsendable): Buffer-localize. (gnus-newsgroup-bookmarks): Buffer-localize. (gnus-newsgroup-dormant): Buffer-localize. (gnus-newsgroup-unseen): Buffer-localize. (gnus-newsgroup-seen): Buffer-localize. (gnus-newsgroup-unexist): Buffer-localize. (gnus-newsgroup-articles): Buffer-localize. (gnus-newsgroup-scored): Buffer-localize. (gnus-newsgroup-headers): Buffer-localize. (gnus-newsgroup-threads): Buffer-localize. (gnus-newsgroup-prepared): Buffer-localize. (gnus-newsgroup-ancient): Buffer-localize. (gnus-newsgroup-sparse): Buffer-localize. (gnus-newsgroup-selection): Buffer-localize. (gnus-current-article): Buffer-localize. (gnus-article-current): Buffer-localize. (gnus-current-headers): Buffer-localize. (gnus-have-all-headers): Buffer-localize. (gnus-last-article): Buffer-localize. (gnus-newsgroup-history): Buffer-localize. (gnus-newsgroup-charset): Buffer-localize. (gnus-summary-local-variables): Delete. (gnus-newsgroup-variables): Delete. (gnus-simplify-subject-fuzzy): gnus-with-temp-buffer, not gnus-set-work-buffer. (gnus-summary-mode-group): Delete. (gnus-summary-mode): Delete gnus-summary-make-local-variables. (gnus-summary-make-local-variables): Delete. (gnus-summary-setup-buffer): Buffer-localize. (gnus-set-global-variables): Delete. (gnus--dummy-data-list): Rectify ancient type error. (gnus-update-summary-mark-positions): gnus-summary-assume-in-summary, not set-buffer. (gnus-summary-insert-line): Describe the error. (gnus-summary-read-group-1): Declutter. (gnus-summary-prepare): Consolidate sorting of message threads. (gnus-gather-threads-by-references): Declutter. (gnus-sort-gathered-threads): Consolidate sorting of message threads. (gnus-summary-assume-in-summary): gnus-summary-assume-in-summary, not set-buffer. (gnus-summary-update-article): gnus-summary-assume-in-summary, not set-buffer. (gnus-select-newsgroup): Whitespace. (gnus-summary-display-make-predicate): Declutter. (gnus-killed-articles): Declutter. (gnus-set-mode-line): Declutter. (gnus-get-newsgroup-headers-xover): Buffer-localize. (gnus-summary-exit): Delete gnus-set-global-variables. (gnus-handle-ephemeral-exit): Delete gnus-set-global-variables. (gnus-summary-display-article): Delete gnus-set-global-variables. (gnus-summary-select-article): gnus-summary-assume-in-summary, not set-buffer. (gnus-summary-force-verify-and-decrypt): Spaces, not tabs. (gnus-summary-next-article): gnus-summary-assume-in-summary, not set-buffer. (gnus-summary-next-page): Delete gnus-set-global-variables. (gnus-summary-enter-digest-group): Delete gnus-set-global-variables. (gnus-summary-edit-article): Delete gnus-set-global-variables. (gnus-summary-catchup): Whitespace. (gnus-summary-catchup-and-exit): Spaces, not tabs. (gnus-summary-sort): Consolidate sorting of message threads. (gnus-offer-save-summaries): Only do for visible buffers. * lisp/gnus/gnus-topic.el (require): Declutter. (gnus-util): Declutter. (subr-x): Declutter. (gnus-group-prepare-topics): Elide gnus-update-format-specifications bogus argument. * lisp/gnus/gnus-undo.el (gnus-util): Declutter. * lisp/gnus/gnus-util.el (require): Declutter. (cl-seq): Declutter. (gnus-push-end): Push at the end. (gnus-parent-id): Declutter. (gnus-buffer-live-p): Clarify. (gnus-work-buffer): gnus-with-temp-buffer, not gnus-set-work-buffer. (gnus-delete-duplicates): Delete. (gnus-assign-former-global): Buffer-localize. (gnus-with-temp-buffer): gnus-with-temp-buffer, not gnus-set-work-buffer. * lisp/gnus/gnus-uu.el (require): Declutter. * lisp/gnus/gnus-win.el (gnus-util): Declutter. (seq): Declutter. (gnus-configure-frame): Increase thread safety. (gnus-configure--frame): Increase thread safety. (gnus-configure-windows): Declutter. * lisp/gnus/gnus.el (require): Declutter. (gnus-util): Declutter. (seq): Declutter. (subr-x): Declutter. (gnus-version-number): Bump version. (nnheader): Declutter. (gnus-secondary-select-methods): Obsolesce gnus-secondary-select-methods. (gnus-select-methods): Obsolesce gnus-secondary-select-methods. (gnus-select-method): Obsolesce gnus-secondary-select-methods. (gnus-redefine-select-method-widget): Obsolesce gnus-secondary-select-methods. (gnus-original-article-buffer): Buffer-localize. (gnus-newsgroup-name): Buffer-localize. (gnus-summary-buffer): Buffer-localize. (gnus-article-buffer): Buffer-localize. (gnus-reffed-article-number): Buffer-localize. (gnus-continuum-version): Bump version. (gnus-summary-buffer-name): Enable backgrounding fetch. (gnus-secondary-method-p): Declutter. (gnus-server-extend-method): Declutter. (gnus-find-method-for-group): Declutter. (gnus-read-method): Obsolesce gnus-secondary-select-methods. (gnus): Declutter. * lisp/gnus/legacy-gnus-agent.el (gnus-util): Declutter. * lisp/gnus/message.el (gnus-msg-inherit-variables): Declutter. (message-pop-to-buffer): Whitespace. (message-followup): Buffer-local variables need to propagate. * lisp/gnus/nnfolder.el (gnus-util): Declutter. * lisp/gnus/nnheader.el (require): Declutter. (nnheader-init-server-buffer): Refactor. (nnheader-prep-server-buffer): Refactor. * lisp/gnus/nnimap.el (require): Declutter. (gnus-util): Declutter. (cl-seq): Declutter. (x-server-version): Declutter. (nnimap-with-context): Prefer this to set-buffer. (nnimap-for-process-buffers): Facilitate iteration over nnimap buffers. (nnimap-connection-alist): Rationalize nnimap. (nnimap-process-buffers): Rationalize nnimap. (nnimap--process-buffer-fmt): Rationalize nnimap. (nnimap-assert-context): Rationalize nnimap. (nnimap-process-buffer-key): Rationalize nnimap. (nnimap-group-to-imap): Rationalize nnimap. (nnimap-buffer): Rationalize nnimap. (nnimap-process-buffer): Rationalize nnimap. (nnimap-retrieve-headers): Rationalize nnimap. (nnimap-open-server): Rationalize nnimap. (nnimap-make-process-buffer): Rationalize nnimap. (nnimap-keepalive): Rationalize nnimap. (nnimap-open-connection): Rationalize nnimap. (nnimap-open-connection-1): Rationalize nnimap. (nnimap-close-server): Rationalize nnimap. (nnimap-server-opened): Rationalize nnimap. (nnimap-request-article): Rationalize nnimap. (nnimap-request-head): Rationalize nnimap. (nnimap-request-articles): Rationalize nnimap. (nnimap-request-group): Rationalize nnimap. (nnimap-request-group-scan): Rationalize nnimap. (nnimap-request-create-group): Rationalize nnimap. (nnimap-request-delete-group): Rationalize nnimap. (nnimap-request-rename-group): Rationalize nnimap. (nnimap-request-expunge-group): Rationalize nnimap. (nnimap-get-flags): Rationalize nnimap. (nnimap-close-group): Rationalize nnimap. (nnimap-request-move-article): Rationalize nnimap. (nnimap-process-expiry-targets): Rationalize nnimap. (nnimap-find-expired-articles): Rationalize nnimap. (nnimap-find-article-by-message-id): Rationalize nnimap. (nnimap-delete-article): Rationalize nnimap. (nnimap-request-update-group-status): Rationalize nnimap. (nnimap-request-set-mark): Rationalize nnimap. (nnimap-request-accept-article): Rationalize nnimap. (nnimap-request-list): Rationalize nnimap. (nnimap-request-newgroups): Rationalize nnimap. (nnimap-retrieve-group-data-early): Rationalize nnimap. (nnimap-finish-retrieve-group-infos): Rationalize nnimap. (nnimap-find-process-buffer): Rationalize nnimap. (nnimap-get-process-buffer): Rationalize nnimap. (nnimap-request-thread): Rationalize nnimap. (nnimap-change-group): Rationalize nnimap. (nnimap-find-connection): Rationalize nnimap. (nnimap-wait-for-response): Rationalize nnimap. (nnimap-split-incoming-mail): Rationalize nnimap. * lisp/gnus/nnmail.el (gnus-util): Declutter. (nnmail-cache-primary-mail-backend): Obsolesce gnus-secondary-select-methods. * lisp/gnus/nnmaildir.el (gnus-util): Declutter. * lisp/gnus/nntp.el (gnus): Declutter. (nntp-retrieval-in-progress): Delete. (nntp-kill-buffer): Declutter. (nntp-find-connection): Make this defun for easier debugging. (nntp-find-connection-buffer): Declutter. (nntp-send-command-and-decode): Declutter. (nntp-retrieve-group-data-early): Declutter. (nntp-finish-retrieve-group-infos): Delete nntp-retrieval-in-progress. (nntp-retrieve-groups): Delete nntp-retrieval-in-progress. (nntp-make-process-buffer): Delete nntp-retrieval-in-progress. (nntp-open-connection): Ensure nntp buffers clean up when killed. * lisp/gnus/nnvirtual.el (gnus): Declutter. (gnus-util): Declutter. * lisp/gnus/nnweb.el (require): Declutter. (gnus-util): Declutter. * lisp/obsolete/nnir.el (nnimap-buffer): Rationalize nnimap. (nnimap-process-buffer): Rationalize nnimap. (nnir-run-imap): Rationalize nnimap. * lisp/org/ol-gnus.el (gnus-util): Declutter. * test/src/process-tests.el (process-test-stopped-pipe): Add test. * test/src/thread-tests.el (eieio): Add test. (ring): Add test. (threads-test-channel): Add test. (threads-test-channel-send): Add test. (threads-test-channel-recv): Add test. (threads-signal-early): Add test. (threads-test-bug33073): Add test. (threads-test-bug36609-signal): Add test. (threads-test-glib-lock): Add test. (threads-test-promiscuous-process): Add test. --- doc/emacs/misc.texi | 3 +- doc/misc/auth.texi | 10 +- doc/misc/gnus.texi | 102 +- etc/gnus/news-server.ast | 2 +- lisp/gnus/gnus-agent.el | 18 +- lisp/gnus/gnus-art.el | 418 ++++--- lisp/gnus/gnus-async.el | 2 - lisp/gnus/gnus-cache.el | 8 +- lisp/gnus/gnus-demon.el | 2 - lisp/gnus/gnus-draft.el | 3 +- lisp/gnus/gnus-fun.el | 1 - lisp/gnus/gnus-group.el | 151 +-- lisp/gnus/gnus-icalendar.el | 2 - lisp/gnus/gnus-int.el | 145 +-- lisp/gnus/gnus-kill.el | 96 +- lisp/gnus/gnus-logic.el | 3 - lisp/gnus/gnus-mlspl.el | 2 +- lisp/gnus/gnus-msg.el | 385 ++++--- lisp/gnus/gnus-notifications.el | 1 - lisp/gnus/gnus-picon.el | 2 - lisp/gnus/gnus-registry.el | 8 +- lisp/gnus/gnus-salt.el | 7 +- lisp/gnus/gnus-score.el | 36 +- lisp/gnus/gnus-search.el | 7 +- lisp/gnus/gnus-spec.el | 79 +- lisp/gnus/gnus-srvr.el | 10 +- lisp/gnus/gnus-start.el | 1171 +++++++++---------- lisp/gnus/gnus-sum.el | 1206 ++++++++------------ lisp/gnus/gnus-topic.el | 8 +- lisp/gnus/gnus-undo.el | 1 - lisp/gnus/gnus-util.el | 55 +- lisp/gnus/gnus-uu.el | 2 - lisp/gnus/gnus-win.el | 61 +- lisp/gnus/gnus.el | 166 +-- lisp/gnus/legacy-gnus-agent.el | 1 - lisp/gnus/message.el | 8 +- lisp/gnus/nnfolder.el | 1 - lisp/gnus/nnheader.el | 22 +- lisp/gnus/nnimap.el | 564 ++++----- lisp/gnus/nnmail.el | 3 +- lisp/gnus/nnmaildir.el | 1 - lisp/gnus/nnrss.el | 1 - lisp/gnus/nntp.el | 85 +- lisp/gnus/nnvirtual.el | 2 - lisp/gnus/nnweb.el | 3 - lisp/obsolete/nnir.el | 6 +- lisp/org/ol-gnus.el | 1 - test/lisp/gnus/gnus-test-select-methods.el | 103 ++ test/lisp/gnus/gnus-tests.el | 92 +- test/src/process-tests.el | 9 + test/src/thread-tests.el | 118 +- 51 files changed, 2572 insertions(+), 2621 deletions(-) create mode 100644 test/lisp/gnus/gnus-test-select-methods.el diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi index 027133cc3a..83b0828351 100644 --- a/doc/emacs/misc.texi +++ b/doc/emacs/misc.texi @@ -97,8 +97,7 @@ Gnus Startup If your system does not have a default news server, or if you wish to use Gnus for reading email, then before invoking @kbd{M-x gnus} you need to tell Gnus where to get news and/or mail. To do this, -customize the variables @code{gnus-select-method} and/or -@code{gnus-secondary-select-methods}. +customize the variable @code{gnus-select-methods}. @iftex See the Gnus manual for details. @end iftex diff --git a/doc/misc/auth.texi b/doc/misc/auth.texi index 034004d1df..ba6a527752 100644 --- a/doc/misc/auth.texi +++ b/doc/misc/auth.texi @@ -280,14 +280,14 @@ Multiple GMail accounts with Gnus @chapter Multiple GMail accounts with Gnus For multiple GMail accounts with Gnus, you have to make two nnimap -entries in your @code{gnus-secondary-select-methods} with distinct +entries in your @code{gnus-select-methods} with distinct names: @example -(setq gnus-secondary-select-methods '((nnimap "gmail" - (nnimap-address "imap.gmail.com")) - (nnimap "gmail2" - (nnimap-address "imap.gmail.com")))) +(setq gnus-select-methods '((nnimap "gmail" + (nnimap-address "imap.gmail.com")) + (nnimap "gmail2" + (nnimap-address "imap.gmail.com")))) @end example Your netrc entries will then be: diff --git a/doc/misc/gnus.texi b/doc/misc/gnus.texi index b63947c044..a59c9bcc1b 100644 --- a/doc/misc/gnus.texi +++ b/doc/misc/gnus.texi @@ -1139,13 +1139,13 @@ Finding the News you want to get your daily dosage of news from, you'd say: @lisp -(setq gnus-select-method '(nntp "news.somewhere.edu")) +(setq gnus-select-method '((nntp "news.somewhere.edu"))) @end lisp If you want to read directly from the local spool, say: @lisp -(setq gnus-select-method '(nnspool "")) +(setq gnus-select-method '((nnspool ""))) @end lisp If you can use a local spool, you probably should, as it will almost @@ -1492,41 +1492,13 @@ Startup Files @section Startup Files @cindex startup files @cindex .newsrc -@cindex .newsrc.el @cindex .newsrc.eld -Most common Unix news readers use a shared startup file called -@file{.newsrc}. This file contains all the information about what -groups are subscribed, and which articles in these groups have been -read. +The @file{.newsrc.eld} contains s-exprs persisting Gnus's state. +Avoid deleting it. -Things got a bit more complicated with @sc{gnus}. In addition to -keeping the @file{.newsrc} file updated, it also used a file called -@file{.newsrc.el} for storing all the information that didn't fit into -the @file{.newsrc} file. (Actually, it also duplicated everything in -the @file{.newsrc} file.) @sc{gnus} would read whichever one of these -files was the most recently saved, which enabled people to swap between -@sc{gnus} and other newsreaders. - -That was kinda silly, so Gnus went one better: In addition to the -@file{.newsrc} and @file{.newsrc.el} files, Gnus also has a file called -@file{.newsrc.eld}. It will read whichever of these files that are most -recent, but it will never write a @file{.newsrc.el} file. You should -never delete the @file{.newsrc.eld} file---it contains much information -not stored in the @file{.newsrc} file. - -@vindex gnus-save-newsrc-file -@vindex gnus-read-newsrc-file -You can turn off writing the @file{.newsrc} file by setting -@code{gnus-save-newsrc-file} to @code{nil}, which means you can delete -the file and save some space, as well as exiting from Gnus faster. -However, this will make it impossible to use other newsreaders than -Gnus. But hey, who would want to, right? Similarly, setting -@code{gnus-read-newsrc-file} to @code{nil} makes Gnus ignore the -@file{.newsrc} file and any @file{.newsrc-SERVER} files, which can be -convenient if you use a different news reader occasionally, and you -want to read a different subset of the available groups with that -news reader. +The @file{.newsrc} is the standard Usenet rcfile, which Gnus will generally +ignore unless the @file{.newsrc.eld} did not assign a @code{gnus-newsrc-alist}. @vindex gnus-save-killed-list If @code{gnus-save-killed-list} (default @code{t}) is @code{nil}, Gnus @@ -1541,34 +1513,8 @@ Startup Files saving. This can be useful in certain obscure situations that involve several servers where not all servers support @code{ask-server}. -@vindex gnus-startup-file -@vindex gnus-backup-startup-file -@vindex version-control -The @code{gnus-startup-file} variable says where the startup files are. -The default value is @file{~/.newsrc}, with the Gnus (El Dingo) startup -file being whatever that one is, with a @samp{.eld} appended. -If you want to keep multiple numbered backups of this file, set -@code{gnus-backup-startup-file}. It respects the same values as the -@code{version-control} variable. - @vindex gnus-save-newsrc-hook -@vindex gnus-save-quick-newsrc-hook -@vindex gnus-save-standard-newsrc-hook -@code{gnus-save-newsrc-hook} is called before saving any of the newsrc -files, while @code{gnus-save-quick-newsrc-hook} is called just before -saving the @file{.newsrc.eld} file, and -@code{gnus-save-standard-newsrc-hook} is called just before saving the -@file{.newsrc} file. The latter two are commonly used to turn version -control on or off. Version control is on by default when saving the -startup files. If you want to turn backup creation off, say something like: - -@lisp -(defun turn-off-backup () - (set (make-local-variable 'backup-inhibited) t)) - -(add-hook 'gnus-save-quick-newsrc-hook 'turn-off-backup) -(add-hook 'gnus-save-standard-newsrc-hook 'turn-off-backup) -@end lisp +@code{gnus-save-newsrc-hook} is called before saving the @file{.newsrc.eld}. @vindex gnus-init-file @vindex gnus-site-init-file @@ -1687,6 +1633,11 @@ Startup Variables @table @code +@item gnus-save-dot-newsrc +If changed to @code{nil} from its default value @code{t}, Gnus will +stop saving @file{.newsrc}, meaning other newsreaders cannot pick +up from where Gnus left off. + @item gnus-before-startup-hook @vindex gnus-before-startup-hook A hook called as the first thing when Gnus is started. @@ -2646,9 +2597,8 @@ Foreign Groups groups under point---@code{gnus-subscribe-newsgroup-method} is not consulted. -Changes from the group editing commands are stored in -@file{~/.newsrc.eld} (@code{gnus-startup-file}). An alternative is the -variable @code{gnus-parameters}, @xref{Group Parameters}. +Changes from the group editing commands are stored in @file{~/.newsrc.eld}. +An alternative is the variable @code{gnus-parameters}, @xref{Group Parameters}. @table @kbd @@ -7323,18 +7273,6 @@ More Threading This is a number that says how much each sub-thread should be indented. The default is 4. -@item gnus-sort-gathered-threads-function -@vindex gnus-sort-gathered-threads-function -Sometimes, particularly with mailing lists, the order in which mails -arrive locally is not necessarily the same as the order in which they -arrived on the mailing list. Consequently, when sorting sub-threads -using the default @code{gnus-thread-sort-by-number}, responses can end -up appearing before the article to which they are responding to. -Setting this variable to an alternate value -(e.g., @code{gnus-thread-sort-by-date}), in a group's parameters or in an -appropriate hook (e.g., @code{gnus-summary-generate-hook}) can produce a -more logical sub-thread ordering in such instances. - @end table @@ -29693,18 +29631,6 @@ Little Disk Space @table @code -@item gnus-save-newsrc-file -If this is @code{nil}, Gnus will never save @file{.newsrc}---it will -only save @file{.newsrc.eld}. This means that you will not be able to -use any other newsreaders than Gnus. This variable is @code{t} by -default. - -@item gnus-read-newsrc-file -If this is @code{nil}, Gnus will never read @file{.newsrc}---it will -only read @file{.newsrc.eld}. This means that you will not be able to -use any other newsreaders than Gnus. This variable is @code{t} by -default. - @item gnus-save-killed-list If this is @code{nil}, Gnus will not save the list of dead groups. You should also set @code{gnus-check-new-newsgroups} to @code{ask-server} diff --git a/etc/gnus/news-server.ast b/etc/gnus/news-server.ast index df0bab4519..555ac47cd9 100644 --- a/etc/gnus/news-server.ast +++ b/etc/gnus/news-server.ast @@ -20,7 +20,7 @@ Port number: @variable{port} @node User name and password @type interstitial -@next +@next (if (assistant-password-required-p) "Enter user name and password" "Want user name and password?") diff --git a/lisp/gnus/gnus-agent.el b/lisp/gnus/gnus-agent.el index cbe3505cd1..2b15ec15dc 100644 --- a/lisp/gnus/gnus-agent.el +++ b/lisp/gnus/gnus-agent.el @@ -22,19 +22,19 @@ ;;; Code: -(require 'gnus) -(require 'gnus-cache) (require 'nnmail) -(require 'nnvirtual) -(require 'gnus-sum) -(require 'gnus-score) -(require 'gnus-srvr) -(require 'gnus-util) (require 'timer) -(eval-when-compile (require 'cl-lib)) +(require 'gnus-start) +(require 'gnus-sum) +(autoload 'gnus-all-score-files "gnus-score") (autoload 'gnus-server-update-server "gnus-srvr") +(autoload 'gnus-server-set-status "gnus-srvr") +(autoload 'gnus-server-named-server "gnus-srvr") +(autoload 'gnus-inews-do-gcc "gnus-msg") +(autoload 'gnus-inews-group-method "gnus-msg") (autoload 'gnus-agent-customize-category "gnus-cus") +(defvar gnus-cache-coding-system) (defcustom gnus-agent-directory (nnheader-concat gnus-directory "agent/") "Where the Gnus agent will store its files." @@ -2063,7 +2063,7 @@ gnus-agent-read-agentview (let (state sequence uncomp) (while alist (setq state (caar alist) - sequence (inline (gnus-uncompress-range (cdar alist))) + sequence (gnus-uncompress-range (cdar alist)) alist (cdr alist)) (while sequence (push (cons (pop sequence) state) uncomp))) diff --git a/lisp/gnus/gnus-art.el b/lisp/gnus/gnus-art.el index f2ec9462c5..011bc0902e 100644 --- a/lisp/gnus/gnus-art.el +++ b/lisp/gnus/gnus-art.el @@ -24,7 +24,6 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) (defvar tool-bar-map) (defvar w3m-minor-mode-map) @@ -41,7 +40,6 @@ w3m-minor-mode-map (require 'mm-uu) (require 'message) (require 'mouse) -(require 'seq) (autoload 'gnus-msg-mail "gnus-msg" nil t) (autoload 'gnus-button-mailto "gnus-msg") @@ -1643,9 +1641,10 @@ gnus-english-month-names "September" "October" "November" "December")) (defvar article-goto-body-goes-to-point-min-p nil) -(defvar gnus-article-wash-types nil) +(defvar-local gnus-article-wash-types nil) (defvar gnus-article-emphasis-alist nil) -(defvar gnus-article-image-alist nil) +(defvar-local gnus-original-group-and-article nil) +(defvar-local gnus-article-image-alist nil) (defvar gnus-article-mime-handle-alist-1 nil) (defvar gnus-treatment-function-alist @@ -1695,9 +1694,9 @@ gnus-treatment-function-alist (gnus-treat-highlight-citation gnus-article-highlight-citation) (gnus-treat-body-boundary gnus-article-treat-body-boundary))) -(defvar gnus-article-mime-handle-alist nil) +(defvar-local gnus-article-mime-handle-alist nil) (defvar article-lapsed-timer nil) -(defvar gnus-article-current-summary nil) +(defvar-local gnus-article-current-summary nil) (defvar gnus-article-mode-syntax-table (let ((table (copy-syntax-table text-mode-syntax-table))) @@ -1719,8 +1718,6 @@ gnus-number-of-articles-to-be-saved (defvar gnus-inhibit-hiding nil) -(defvar gnus-article-edit-mode nil) - ;;; Macros for dealing with the article buffer. (defmacro gnus-with-article-headers (&rest forms) @@ -4494,74 +4491,72 @@ gnus-article-mode (setq-local nobreak-char-display nil) ;; Enable `gnus-article-remove-images' to delete images shr.el renders. (setq-local shr-put-image-function #'gnus-shr-put-image) + (setq show-trailing-whitespace nil) (unless gnus-article-show-cursor (setq cursor-in-non-selected-windows nil)) (gnus-set-default-directory) (buffer-disable-undo) - (setq show-trailing-whitespace nil) (mm-enable-multibyte)) +(defun gnus-article-setup-buffer-ensure (summary-buffer + newsgroup-name + article-buffer-name + original-article-buffer-name) + "Refactor." + (gnus-summary-assume-in-summary + (with-current-buffer (gnus-get-buffer-create original-article-buffer-name) + (mm-enable-multibyte) + (setq major-mode 'gnus-original-article-mode)) + + (with-current-buffer (gnus-get-buffer-create article-buffer-name) + (dolist (buffer `(,(current-buffer) ,summary-buffer)) + (gnus-assign-former-global 'gnus-article-buffer + article-buffer-name + buffer) + (gnus-assign-former-global 'gnus-original-article-buffer + original-article-buffer-name + buffer) + (gnus-assign-former-global 'gnus-newsgroup-name + newsgroup-name + buffer) + (gnus-assign-former-global 'gnus-summary-buffer + summary-buffer + buffer)) + (gnus-article-stop-animations) + (mm-destroy-parts gnus-article-mime-handles) + (setq gnus-article-mime-handles nil + gnus-article-mime-handle-alist nil) + (unless (derived-mode-p 'gnus-article-mode) + (gnus-article-mode)) + (setq truncate-lines gnus-article-truncate-lines) + (gnus-summary-set-local-parameters newsgroup-name) + (when article-lapsed-timer + (gnus-stop-date-timer)) + (when gnus-article-update-date-headers + (gnus-start-date-timer gnus-article-update-date-headers)) + (current-buffer)))) + (defun gnus-article-setup-buffer () "Initialize the article buffer." - (let* ((name (if gnus-single-article-buffer "*Article*" - (concat "*Article " gnus-newsgroup-name "*"))) - (original - (progn (string-match "\\*Article" name) - (concat " *Original Article" - (substring name (match-end 0)))))) - (setq gnus-article-buffer name) - (setq gnus-original-article-buffer original) - (setq gnus-article-mime-handle-alist nil) - (with-current-buffer gnus-summary-buffer - ;; This might be a variable local to the summary buffer. - (unless gnus-single-article-buffer - (setq gnus-article-buffer name) - (setq gnus-original-article-buffer original) - (gnus-set-global-variables))) + (gnus-summary-assume-in-summary (gnus-article-setup-highlight-words) - ;; Init original article buffer. - (with-current-buffer (gnus-get-buffer-create gnus-original-article-buffer) - (mm-enable-multibyte) - (setq major-mode 'gnus-original-article-mode) - (make-local-variable 'gnus-original-article)) - (if (and (get-buffer name) - (with-current-buffer name - (if gnus-article-edit-mode - (if (y-or-n-p "Article mode edit in progress; discard? ") - (progn - (set-buffer-modified-p nil) - (gnus-kill-buffer name) - (message "") - nil) - (error "Action aborted")) - t))) - (let ((summary gnus-summary-buffer)) - (with-current-buffer name - (setq-local gnus-article-edit-mode nil) - (gnus-article-stop-animations) - (when gnus-article-mime-handles - (mm-destroy-parts gnus-article-mime-handles) - (setq gnus-article-mime-handles nil)) - ;; Set it to nil in article-buffer! - (setq gnus-article-mime-handle-alist nil) - (buffer-disable-undo) - (setq buffer-read-only t) - (unless (derived-mode-p 'gnus-article-mode) - (gnus-article-mode)) - (setq-local gnus-summary-buffer summary) - (setq truncate-lines gnus-article-truncate-lines) - (current-buffer))) - (let ((summary gnus-summary-buffer)) - (with-current-buffer (gnus-get-buffer-create name) - (gnus-article-mode) - (setq truncate-lines gnus-article-truncate-lines) - (setq-local gnus-summary-buffer summary) - (gnus-summary-set-local-parameters gnus-newsgroup-name) - (when article-lapsed-timer - (gnus-stop-date-timer)) - (when gnus-article-update-date-headers - (gnus-start-date-timer gnus-article-update-date-headers)) - (current-buffer)))))) + (let* ((summary gnus-summary-buffer) + (group gnus-newsgroup-name) + (name (if gnus-single-article-buffer "*Article*" + (concat "*Article " group "*"))) + (original (progn (string-match "\\*Article" name) + (concat " *Original Article" + (substring name (match-end 0)))))) + (when-let ((existing-buffer (get-buffer name))) + (with-current-buffer existing-buffer + (when (eq major-mode 'gnus-article-edit-mode) + (if (y-or-n-p "Article mode edit in progress; discard? ") + (progn + (set-buffer-modified-p nil) + (gnus-kill-buffer existing-buffer) + (message "")) + (error "Action aborted"))))) + (gnus-article-setup-buffer-ensure summary group name original)))) (defun gnus-article-stop-animations () (cancel-function-timers 'image-animate-timeout)) @@ -4593,113 +4588,116 @@ gnus-article-prepare ARTICLE should either be an article number or a Message-ID. If ARTICLE is an id, HEADER should be the article headers. If ALL-HEADERS is non-nil, no headers are hidden." - (save-excursion ;FIXME: Shouldn't that be save-current-buffer? - ;; Make sure we start in a summary buffer. - (unless (derived-mode-p 'gnus-summary-mode) - (set-buffer gnus-summary-buffer)) - (setq gnus-summary-buffer (current-buffer)) - (let* ((summary-buffer (current-buffer)) - (gnus-tmp-internal-hook gnus-article-internal-prepare-hook) - (group gnus-newsgroup-name) - result) - (save-excursion - (gnus-article-setup-buffer) - (set-buffer gnus-article-buffer) - ;; Deactivate active regions. - (when transient-mark-mode - (setq mark-active nil)) - (if (not (setq result (let ((inhibit-read-only t)) - (gnus-request-article-this-buffer - article group)))) - ;; There is no such article. - (save-excursion - (when (and (numberp article) - (not (memq article gnus-newsgroup-sparse))) - (setq gnus-article-current - (cons gnus-newsgroup-name article)) - (set-buffer gnus-summary-buffer) - (setq gnus-current-article article) - (if (and (memq article gnus-newsgroup-undownloaded) - (not (gnus-online (gnus-find-method-for-group - gnus-newsgroup-name)))) - (progn - (gnus-summary-set-agent-mark article) - (message "Message marked for downloading")) - (gnus-summary-mark-article article gnus-canceled-mark) - (unless (memq article gnus-newsgroup-sparse) - (gnus-error 1 "No such article (may have expired or been canceled)"))))) - (if (or (eq result 'pseudo) - (eq result 'nneething)) - (progn - (with-current-buffer summary-buffer - (push article gnus-newsgroup-history) - (setq gnus-last-article gnus-current-article - gnus-current-article 0 - gnus-current-headers nil - gnus-article-current nil) - (if (eq result 'nneething) - (gnus-configure-windows 'summary) - (gnus-configure-windows 'article)) - (gnus-set-global-variables)) - (let ((gnus-article-mime-handle-alist-1 - gnus-article-mime-handle-alist)) - (gnus-set-mode-line 'article))) - ;; The result from the `request' was an actual article - - ;; or at least some text that is now displayed in the - ;; article buffer. - (when (and (numberp article) - (not (eq article gnus-current-article))) - ;; Seems like a new article has been selected. - ;; `gnus-current-article' must be an article number. - (with-current-buffer summary-buffer - (push article gnus-newsgroup-history) - (setq gnus-last-article gnus-current-article - gnus-current-article article - gnus-current-headers - (gnus-summary-article-header gnus-current-article) - gnus-article-current - (cons gnus-newsgroup-name gnus-current-article)) - (unless (mail-header-p gnus-current-headers) - (setq gnus-current-headers nil)) - (gnus-summary-goto-subject gnus-current-article) - (when (gnus-summary-show-thread) - ;; If the summary buffer really was folded, the - ;; previous goto may not actually have gone to - ;; the right article, but the thread root instead. - ;; So we go again. - (gnus-summary-goto-subject gnus-current-article)) - (gnus-run-hooks 'gnus-mark-article-hook) - (gnus-set-mode-line 'summary) - (when (gnus-visual-p 'article-highlight 'highlight) - (gnus-run-hooks 'gnus-visual-mark-article-hook)) - ;; Set the global newsgroup variables here. - (gnus-set-global-variables) - (setq gnus-have-all-headers - (or all-headers gnus-show-all-headers)))) - (save-excursion - (gnus-configure-windows 'article)) - (when (or (numberp article) - (stringp article)) - (gnus-article-prepare-display) - ;; Do page break. - (goto-char (point-min)) - (when gnus-break-pages - (gnus-narrow-to-page))) - (let ((gnus-article-mime-handle-alist-1 - gnus-article-mime-handle-alist)) - (gnus-set-mode-line 'article)) - (article-goto-body) - (unless (bobp) - (forward-line -1)) - (set-window-point (get-buffer-window (current-buffer)) (point)) - (gnus-configure-windows 'article) - ;; Make sure the article begins with the top of the header. - (let ((window (get-buffer-window gnus-article-buffer))) - (when window - (with-current-buffer (window-buffer window) - (set-window-point window (point-min))))) - (gnus-run-hooks 'gnus-article-prepare-hook) - t)))))) + (gnus-summary-assume-in-summary + (gnus-article-setup-buffer) + (let ((group gnus-newsgroup-name) + (summary (current-buffer)) + result) + (with-current-buffer gnus-article-buffer + ;; Deactivate active regions. + (when transient-mark-mode + (setq mark-active nil)) + (cond ((not (setq result (let ((inhibit-read-only t)) + (gnus-request-article-this-buffer + article group)))) + (with-current-buffer summary + (when (and (numberp article) + (not (memq article gnus-newsgroup-sparse))) + (setq gnus-article-current + (cons gnus-newsgroup-name article)) + (setq gnus-current-article article) + (if (and (memq article gnus-newsgroup-undownloaded) + (not (gnus-online (gnus-find-method-for-group + gnus-newsgroup-name)))) + (progn + (gnus-summary-set-agent-mark article) + (message "Message marked for downloading")) + (gnus-summary-mark-article article gnus-canceled-mark) + (unless (memq article gnus-newsgroup-sparse) + (gnus-error + 1 + "No such article (may have expired or been canceled)")))))) + ((or (eq result 'pseudo) (eq result 'nneething)) + (with-current-buffer summary + (push article gnus-newsgroup-history) + (setq gnus-last-article gnus-current-article + gnus-current-article 0 + gnus-current-headers nil + gnus-article-current nil) + (if (eq result 'nneething) + (gnus-configure-windows 'summary) + (gnus-configure-windows 'article))) + (let ((gnus-article-mime-handle-alist-1 + gnus-article-mime-handle-alist)) + (gnus-set-mode-line 'article))) + ;; The result from the `request' was an actual article - + ;; or at least some text that is now displayed in the + ;; article buffer. + (t + (with-current-buffer summary + (when (and (numberp article) + (not (eq article gnus-current-article))) + ;; Seems like a new article has been selected. + ;; `gnus-current-article' must be an article number. + (push article gnus-newsgroup-history) + ;; TODO: a more elegant way to replicate former globals + (dolist (buffer `(,(get-buffer gnus-article-buffer) + ,(get-buffer summary))) + (gnus-assign-former-global + 'gnus-last-article + gnus-current-article + buffer) + (gnus-assign-former-global + 'gnus-current-article + article + buffer) + (gnus-assign-former-global + 'gnus-current-headers + (gnus-summary-article-header article) + buffer) + (gnus-assign-former-global + 'gnus-article-current + (cons gnus-newsgroup-name article) + buffer)) + (unless (mail-header-p gnus-current-headers) + (setq gnus-current-headers nil)) + (gnus-summary-goto-subject gnus-current-article) + (when (gnus-summary-show-thread) + ;; If the summary buffer really was folded, the + ;; previous goto may not actually have gone to + ;; the right article, but the thread root instead. + ;; So we go again. + (gnus-summary-goto-subject gnus-current-article)) + (gnus-run-hooks 'gnus-mark-article-hook) + (gnus-set-mode-line 'summary) + (when (gnus-visual-p 'article-highlight 'highlight) + (gnus-run-hooks 'gnus-visual-mark-article-hook)) + (setq gnus-have-all-headers + (or all-headers gnus-show-all-headers)))) + (save-excursion + (gnus-configure-windows 'article)) + (when (or (numberp article) + (stringp article)) + (gnus-article-prepare-display) + ;; Do page break. + (goto-char (point-min)) + (when gnus-break-pages + (gnus-narrow-to-page))) + (let ((gnus-article-mime-handle-alist-1 + gnus-article-mime-handle-alist)) + (gnus-set-mode-line 'article)) + (article-goto-body) + (unless (bobp) + (forward-line -1)) + (set-window-point (get-buffer-window (current-buffer)) (point)) + (gnus-configure-windows 'article) + ;; Make sure the article begins with the top of the header. + (let ((window (get-buffer-window gnus-article-buffer))) + (when window + (with-current-buffer (window-buffer window) + (set-window-point window (point-min))))) + (gnus-run-hooks 'gnus-article-prepare-hook) + t)))))) ;;;###autoload (defun gnus-article-prepare-display () @@ -4714,7 +4712,7 @@ gnus-article-prepare-display (setq buffer-read-only nil gnus-article-wash-types nil gnus-article-image-alist nil) - (gnus-run-hooks 'gnus-tmp-internal-hook) + (gnus-run-hooks 'gnus-article-internal-prepare-hook) (when gnus-display-mime-function (funcall gnus-display-mime-function)) ;; Add attachment buttons to the header. @@ -6683,7 +6681,6 @@ gnus-article-show-summary (interactive nil gnus-article-mode) (if (not (gnus-buffer-live-p gnus-summary-buffer)) (error "There is no summary buffer for this article buffer") - (gnus-article-set-globals) (gnus-configure-windows 'article) (gnus-summary-goto-subject gnus-current-article) (gnus-summary-position-point))) @@ -6960,16 +6957,16 @@ gnus-article-followup-with-original (interactive nil gnus-article-mode) (let ((article (cdr gnus-article-current)) contents) - (if (not (and transient-mark-mode mark-active)) - (with-current-buffer gnus-summary-buffer - (gnus-summary-followup (list (list article)))) - (setq contents (buffer-substring (point) (mark t))) - ;; Deactivate active regions. - (when transient-mark-mode - (setq mark-active nil)) - (with-current-buffer gnus-summary-buffer - (gnus-summary-followup - (list (list article contents))))))) + (if (not (and transient-mark-mode mark-active)) + (with-current-buffer gnus-summary-buffer + (gnus-summary-followup (list (list article)))) + (setq contents (buffer-substring (point) (mark t))) + ;; Deactivate active regions. + (when transient-mark-mode + (setq mark-active nil)) + (with-current-buffer gnus-summary-buffer + (gnus-summary-followup + (list (list article contents))))))) (defun gnus-article-hide (&optional arg force) "Hide all the gruft in the current article. @@ -7059,8 +7056,10 @@ gnus-request-article-this-buffer ((and (get-buffer gnus-original-article-buffer) (numberp article) (with-current-buffer gnus-original-article-buffer - (and (equal (car gnus-original-article) group) - (eq (cdr gnus-original-article) article)))) + (when (consp gnus-original-group-and-article) + (cl-destructuring-bind (ogroup . oarticle) + gnus-original-group-and-article + (and (equal ogroup group) (eq oarticle article)))))) (insert-buffer-substring gnus-original-article-buffer) 'article) ;; Check the backlog. @@ -7131,24 +7130,23 @@ gnus-request-article-this-buffer ;; Take the article from the original article buffer ;; and place it in the buffer it's supposed to be in. (when (and (get-buffer gnus-article-buffer) - (equal (buffer-name (current-buffer)) - (buffer-name (get-buffer gnus-article-buffer)))) - (save-excursion - (if (get-buffer gnus-original-article-buffer) - (set-buffer gnus-original-article-buffer) - (set-buffer (gnus-get-buffer-create gnus-original-article-buffer)) - (buffer-disable-undo) - (setq major-mode 'gnus-original-article-mode) - (setq buffer-read-only t)) - (let ((inhibit-read-only t)) - (erase-buffer) - (insert-buffer-substring gnus-article-buffer)) - (setq gnus-original-article (cons group article))) - - ;; Decode charsets. - (run-hooks 'gnus-article-decode-hook) - ;; Mark article as decoded or not. - (setq gnus-article-decoded-p gnus-article-decode-hook)) + (equal (buffer-name (current-buffer)) + (buffer-name (get-buffer gnus-article-buffer)))) + (unless (get-buffer gnus-original-article-buffer) + (with-current-buffer (gnus-get-buffer-create gnus-original-article-buffer) + (buffer-disable-undo) + (setq major-mode 'gnus-original-article-mode) + (setq buffer-read-only t))) + (let ((article-buffer gnus-article-buffer)) + (with-current-buffer (get-buffer gnus-original-article-buffer) + (let ((inhibit-read-only t)) + (erase-buffer) + (insert-buffer-substring article-buffer)) + (setq gnus-original-group-and-article (cons group article)))) + ;; Decode charsets. + (run-hooks 'gnus-article-decode-hook) + ;; Mark article as decoded or not. + (setq gnus-article-decoded-p gnus-article-decode-hook)) ;; Update sparse articles. (when (and do-update-line @@ -7261,7 +7259,6 @@ gnus-article-edit-mode (make-local-variable 'gnus-prev-winconf) (setq-local font-lock-defaults '(message-font-lock-keywords t)) (setq-local mail-header-separator "") - (setq-local gnus-article-edit-mode t) (mml-mode) (setq buffer-read-only nil) (buffer-enable-undo) @@ -7335,7 +7332,7 @@ gnus-article-edit-done (defun gnus-flush-original-article-buffer () (when (get-buffer gnus-original-article-buffer) (with-current-buffer gnus-original-article-buffer - (setq gnus-original-article nil)))) + (setq gnus-original-group-and-article nil)))) (defun gnus-article-edit-exit () "Exit the article editing without updating." @@ -8134,10 +8131,6 @@ gnus-article-copy-string ;;; Internal functions: -(defun gnus-article-set-globals () - (with-current-buffer gnus-summary-buffer - (gnus-set-global-variables))) - (defun gnus-signature-toggle (end) (gnus-with-article-buffer (let ((inhibit-point-motion-hooks t)) @@ -8628,7 +8621,6 @@ gnus-article-encrypt-body (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets) (summary-buffer gnus-summary-buffer) references point) - (gnus-set-global-variables) (when (gnus-group-read-only-p) (error "The current newsgroup does not support article encrypt")) (gnus-summary-show-article t) diff --git a/lisp/gnus/gnus-async.el b/lisp/gnus/gnus-async.el index fefd02c7bf..239e5f67bd 100644 --- a/lisp/gnus/gnus-async.el +++ b/lisp/gnus/gnus-async.el @@ -24,8 +24,6 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'gnus) (require 'gnus-sum) (require 'nntp) diff --git a/lisp/gnus/gnus-cache.el b/lisp/gnus/gnus-cache.el index 34dba54c11..33fcfd8931 100644 --- a/lisp/gnus/gnus-cache.el +++ b/lisp/gnus/gnus-cache.el @@ -24,9 +24,6 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - -(require 'gnus) (require 'gnus-sum) (declare-function gnus-agent-load-alist "gnus-agent" (group)) @@ -81,7 +78,7 @@ gnus-cache-coding-system ;;; Internal variables. -(defvar gnus-cache-removable-articles nil) +(defvar-local gnus-cache-removable-articles nil) (defvar gnus-cache-buffer nil) (defvar gnus-cache-active-hashtb nil) (defvar gnus-cache-active-altered nil) @@ -630,8 +627,7 @@ gnus-cache-read-active ;; There is no active file, so we generate one. (gnus-cache-generate-active) ;; We simply read the active file. - (save-excursion - (gnus-set-work-buffer) + (gnus-with-temp-buffer (nnheader-insert-file-contents gnus-cache-active-file) (gnus-active-to-gnus-format nil (setq gnus-cache-active-hashtb diff --git a/lisp/gnus/gnus-demon.el b/lisp/gnus/gnus-demon.el index e99247c0ca..bcc70654b0 100644 --- a/lisp/gnus/gnus-demon.el +++ b/lisp/gnus/gnus-demon.el @@ -24,8 +24,6 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'gnus) (require 'gnus-int) (require 'nnheader) diff --git a/lisp/gnus/gnus-draft.el b/lisp/gnus/gnus-draft.el index 9a0f21359f..6d2c2fd57a 100644 --- a/lisp/gnus/gnus-draft.el +++ b/lisp/gnus/gnus-draft.el @@ -24,12 +24,11 @@ ;;; Code: -(require 'gnus) (require 'gnus-sum) -(require 'message) (require 'gnus-msg) (require 'nndraft) (require 'gnus-agent) +(require 'gnus-cache) ;;; Draft minor mode diff --git a/lisp/gnus/gnus-fun.el b/lisp/gnus/gnus-fun.el index 8bca4ffe38..923aa1ec38 100644 --- a/lisp/gnus/gnus-fun.el +++ b/lisp/gnus/gnus-fun.el @@ -25,7 +25,6 @@ ;;; Code: (require 'mm-util) -(require 'gnus-util) (require 'gnus) (defvar gnus-face-properties-alist) diff --git a/lisp/gnus/gnus-group.el b/lisp/gnus/gnus-group.el index c8b95d9185..bedbf19ea2 100644 --- a/lisp/gnus/gnus-group.el +++ b/lisp/gnus/gnus-group.el @@ -35,10 +35,10 @@ (require 'gnus-undo) (require 'gmm-utils) (require 'time-date) +(require 'mm-url) +(require 'subr-x) (eval-when-compile - (require 'mm-url) - (require 'subr-x) (with-suppressed-warnings ((lexical features)) (dlet ((features (cons 'gnus-group features))) (require 'gnus-sum)))) @@ -1136,7 +1136,7 @@ gnus-group-mode (setq truncate-lines t) (setq show-trailing-whitespace nil) (gnus-set-default-directory) - (gnus-update-format-specifications nil 'group 'group-mode) + (gnus-update-format-specifications 'group 'group-mode) (gnus-update-group-mark-positions) (when gnus-use-undo (gnus-undo-mode 1)) @@ -1144,19 +1144,17 @@ gnus-group-mode (gnus-child-mode))) (defun gnus-update-group-mark-positions () - (save-excursion - (let ((gnus-process-mark ?\200) - (gnus-group-update-hook nil) - (gnus-group-marked '("dummy.group")) - (gnus-active-hashtb (gnus-make-hashtable 10))) - (gnus-set-active "dummy.group" '(0 . 0)) - (gnus-set-work-buffer) - (gnus-group-insert-group-line "dummy.group" 0 nil 0 nil) - (goto-char (point-min)) + (let ((gnus-process-mark ?\200) + (gnus-group-update-hook nil) + (gnus-group-marked '("dummy.group")) + (gnus-active-hashtb (gnus-make-hashtable 10))) + (gnus-set-active "dummy.group" '(0 . 0)) + (gnus-with-temp-buffer + (save-excursion + (gnus-group-insert-group-line "dummy.group" 0 nil 0 nil)) (setq gnus-group-mark-positions - (list (cons 'process (and (search-forward - (string gnus-process-mark) nil t) - (- (point) (point-min) 1)))))))) + (list (cons 'process (when (search-forward (string gnus-process-mark) nil t) + (- (point) (point-min) 1)))))))) (defun gnus-mouse-pick-group (e) "Enter the group under the mouse pointer." @@ -1248,27 +1246,19 @@ gnus-group-list-groups unread (cdr gnus-group-list-mode))) (setq level (gnus-group-default-level level)) (gnus-group-setup-buffer) - (gnus-update-format-specifications nil 'group 'group-mode) - (let ((case-fold-search nil) - (props (text-properties-at (point-at-bol))) - (empty (= (point-min) (point-max))) + (gnus-update-format-specifications 'group 'group-mode) + (let ((props (text-properties-at (point-at-bol))) (group (gnus-group-group-name)) - number) - (set-buffer gnus-group-buffer) - (setq number (funcall gnus-group-prepare-function level unread lowest)) + (number (funcall gnus-group-prepare-function level unread lowest)) + case-fold-search) (when (or (and (numberp number) (zerop number)) (zerop (buffer-size))) - ;; No groups in the buffer. (gnus-message 5 "%s" gnus-no-groups-message)) - ;; We have some groups displayed. - (goto-char (point-max)) (when (or (not gnus-group-goto-next-group-function) (not (funcall gnus-group-goto-next-group-function group props))) (cond - (empty - (goto-char (point-min))) ((not group) ;; Go to the first group with unread articles. (gnus-group-search-forward t)) @@ -1277,8 +1267,7 @@ gnus-group-list-groups ;; has disappeared in the new listing, try to find the next ;; one. If no next one can be found, just leave point at the ;; first newsgroup in the buffer. - (when (not (gnus-text-property-search - 'gnus-group group nil 'goto)) + (unless (gnus-text-property-search 'gnus-group group nil 'goto) (let ((groups (cdr-safe (member group gnus-group-list)))) (while (and groups (not (gnus-text-property-search @@ -1287,7 +1276,8 @@ gnus-group-list-groups (unless groups (goto-char (point-max)) (forward-line -1))))))) - ;; Adjust cursor point. + (when (eobp) + (goto-char (point-min))) (gnus-group-position-point))) (defun gnus-group-list-level (level &optional all) @@ -1590,7 +1580,7 @@ gnus-group-insert-group-line (setq end (point)) (gnus-group--setup-tool-bar-update beg end) (forward-line -1) - (when (inline (gnus-visual-p 'group-highlight 'highlight)) + (when (gnus-visual-p 'group-highlight 'highlight) (gnus-group-highlight-line gnus-tmp-group beg end)) (gnus-run-hooks 'gnus-group-update-hook) (forward-line))) @@ -1615,8 +1605,7 @@ gnus-group-update-eval-form (let* ((entry (gnus-group-entry group)) (active (gnus-active group)) (info (nth 1 entry)) - (method (inline (gnus-server-get-method - group (gnus-info-method info)))) + (method (gnus-server-get-method group (gnus-info-method info))) (marked (gnus-info-marks info)) (env (list @@ -2725,17 +2714,13 @@ gnus-group-best-unread-group (defun gnus-group-first-unread-group () "Go to the first group with unread articles." (interactive nil gnus-group-mode) - (prog1 - (let ((opoint (point)) - unread) - (goto-char (point-min)) - (if (or (eq (setq unread (gnus-group-group-unread)) t) ; Not active. - (and (numberp unread) ; Not a topic. - (not (zerop unread))) ; Has unread articles. - (zerop (gnus-group-next-unread-group 1))) ; Next unread group. - (point) ; Success. - (goto-char opoint) - nil)) ; Not success. + (let ((opoint (point))) + (goto-char (save-excursion + (goto-char (point-min)) + (let ((unread (gnus-group-group-unread))) + (cond ((and (numberp unread) (not (zerop unread))) + (point)) + (t opoint))))) (gnus-group-position-point))) (defun gnus-group-enter-server-mode () @@ -4137,7 +4122,7 @@ gnus-group-list-active "\n")) (list 'gnus-group group 'gnus-unread t - 'gnus-level (inline (gnus-group-level group))))) + 'gnus-level (gnus-group-level group)))) (goto-char (point-min)))) (defun gnus-activate-all-groups (level) @@ -4167,17 +4152,13 @@ gnus-group-get-new-news (unless gnus-child (gnus-parent-read-child-newsrc)) - (gnus-get-unread-articles (gnus-group-default-level arg t) - nil one-level) + (gnus-get-unread-articles arg nil one-level) ;; If the user wants it, we scan for new groups. (when (eq gnus-check-new-newsgroups 'always) (gnus-find-new-newsgroups)) - (gnus-check-reasonable-setup) - (gnus-run-hooks 'gnus-after-getting-new-news-hook) - (gnus-group-list-groups (and (numberp arg) - (max (car gnus-group-list-mode) arg))))) + (gnus-check-reasonable-setup))) (defun gnus-group-get-new-news-this-group (&optional n dont-scan) "Check for newly arrived news in the current group (and the N-1 next groups). @@ -4493,10 +4474,9 @@ gnus-group-quit (zerop (buffer-size)) (not (gnus-server-opened gnus-select-method)) gnus-expert-user - (not gnus-current-startup-file) (gnus-yes-or-no-p (format "Quit reading news without saving %s? " - (file-name-nondirectory gnus-current-startup-file)))) + (file-name-nondirectory gnus-newsrc-file)))) (gnus-run-hooks 'gnus-exit-gnus-hook) (gnus-configure-windows 'group t) (when (and (gnus-buffer-live-p gnus-dribble-buffer) @@ -4551,8 +4531,7 @@ gnus-group-set-info (let* ((entry (gnus-group-entry (or method-only-group (gnus-info-group info)))) (part-info info) - (info (if method-only-group (nth 1 entry) info)) - method) + (info (if method-only-group (nth 1 entry) info))) (when method-only-group (unless entry (error "Trying to change non-existent group %s" method-only-group)) @@ -4569,45 +4548,35 @@ gnus-group-set-info (unless entry ;; This is a new group, so we just create it. (with-current-buffer gnus-group-buffer - (setq method (gnus-info-method info)) - (when (gnus-server-equal method "native") - (setq method nil)) - (with-current-buffer gnus-group-buffer - (if method - ;; It's a foreign group... + (let ((method (gnus-info-method info))) + (with-current-buffer gnus-group-buffer + (if (gnus-server-equal method "native") + (gnus-group-make-group (gnus-info-group info)) (gnus-group-make-group (gnus-group-real-name (gnus-info-group info)) - (if (stringp method) method + (if (stringp method) + method (prin1-to-string (car method))) - (and (consp method) - (nth 1 (gnus-info-method info))) - nil) - ;; It's a native group. - (gnus-group-make-group (gnus-info-group info) nil nil nil))) - (gnus-message 6 "Note: New group created") - (setq entry - (gnus-group-entry (gnus-group-prefixed-name - (gnus-group-real-name (gnus-info-group info)) - (or (gnus-info-method info) gnus-select-method)))))) - ;; Whether it was a new group or not, we now have the entry, so we - ;; can do the update. - (if entry - (progn - (setcar (nthcdr 1 entry) info) - (when (and (not (eq (car entry) t)) - (gnus-active (gnus-info-group info))) - (setcar entry (length - (gnus-list-of-unread-articles (car info))))) - ;; The above `setcar' will only affect the hashtable, not - ;; the alist: update the alist separately, but only if - ;; it's been initialized. - (when gnus-newsrc-alist - (push info (cdr (setq gnus-newsrc-alist - (remove (assoc-string - (gnus-info-group info) - gnus-newsrc-alist) - gnus-newsrc-alist)))))) - (error "No such group: %s" (gnus-info-group info)))))) + (when (consp method) + (nth 1 (gnus-info-method info)))))) + (setq entry + (gnus-group-entry (gnus-group-prefixed-name + (gnus-group-real-name (gnus-info-group info)) + (or method gnus-select-method)))) + (gnus-message 6 "Note: New group created")))) + (setcar (nthcdr 1 entry) info) + (when (and (not (eq (car entry) t)) + (gnus-active (gnus-info-group info))) + (setcar entry (length + (gnus-list-of-unread-articles (car info))))) + ;; How `gnus-newsrc-hashtb' and `gnus-newsrc-alist' are + ;; dutifully kept in-sync is anyone's guess... + (when gnus-newsrc-alist + (push info (cdr (setq gnus-newsrc-alist + (remove (assoc-string + (gnus-info-group info) + gnus-newsrc-alist) + gnus-newsrc-alist)))))))) ;; Ad-hoc function for inserting data from a different newsrc.eld ;; file. Use with caution, if at all. diff --git a/lisp/gnus/gnus-icalendar.el b/lisp/gnus/gnus-icalendar.el index 1b2743c148..855cc630a0 100644 --- a/lisp/gnus/gnus-icalendar.el +++ b/lisp/gnus/gnus-icalendar.el @@ -42,8 +42,6 @@ (require 'gnus-sum) (require 'gnus-art) -(eval-when-compile (require 'cl-lib)) - (defun gnus-icalendar-find-if (pred seq) (catch 'found (while seq diff --git a/lisp/gnus/gnus-int.el b/lisp/gnus/gnus-int.el index 01053797b3..e71be0518f 100644 --- a/lisp/gnus/gnus-int.el +++ b/lisp/gnus/gnus-int.el @@ -33,6 +33,11 @@ (autoload 'gnus-agent-regenerate-group "gnus-agent") (autoload 'gnus-agent-read-servers-validate-native "gnus-agent") (autoload 'gnus-agent-possibly-synchronize-flags-server "gnus-agent") +(autoload 'gnus-agent-unfetch-articles "gnus-agent") +(autoload 'gnus-summary-select-article "gnus-sum") +(autoload 'gnus-summary-insert-subject "gnus-sum") +(autoload 'gnus-summary-setup-buffer "gnus-sum") +(autoload 'gnus-summary-read-group-1 "gnus-sum") (defcustom gnus-open-server-hook nil "Hook called just before opening connection to the news server." @@ -84,7 +89,7 @@ gnus-server-opened (if (stringp command-method) (gnus-server-to-method command-method) command-method))) - (funcall (inline (gnus-get-function gnus-command-method 'server-opened)) + (funcall (gnus-get-function gnus-command-method 'server-opened) (nth 1 gnus-command-method))))) (defun gnus-status-message (command-method) @@ -105,73 +110,69 @@ gnus-status-message (defun gnus-start-news-server (&optional confirm) "Open a method for getting news. If CONFIRM is non-nil, the user will be asked for an NNTP server." - (let (how) - (if gnus-current-select-method - ;; Stream is already opened. - nil - ;; Open NNTP server. - (when confirm - ;; Read server name with completion. - (setq gnus-nntp-server - (gnus-completing-read "NNTP server" - (cons gnus-nntp-server - (if (boundp 'gnus-secondary-servers) - gnus-secondary-servers)) - nil gnus-nntp-server))) - - (when (and gnus-nntp-server - (stringp gnus-nntp-server) - (not (string= gnus-nntp-server ""))) - (setq gnus-select-method - (cond ((or (string= gnus-nntp-server "") - (string= gnus-nntp-server "::")) - (list 'nnspool (system-name))) - ((string-match "^:" gnus-nntp-server) - (list 'nnmh gnus-nntp-server - (list 'nnmh-directory - (file-name-as-directory - (expand-file-name - (substring gnus-nntp-server 1) "~/"))) - (list 'nnmh-get-new-mail nil))) - (t - (list 'nntp gnus-nntp-server))))) - - (setq how (car gnus-select-method)) - (cond - ((eq how 'nnspool) - (require 'nnspool) - (gnus-message 5 "Looking up local news spool...")) - ((eq how 'nnmh) - (require 'nnmh) - (gnus-message 5 "Looking up mh spool...")) - (t - (require 'nntp))) - (setq gnus-current-select-method gnus-select-method) - (gnus-run-hooks 'gnus-open-server-hook) + (unless gnus-current-select-method + (when confirm + ;; Read server name with completion. + (setq gnus-nntp-server + (gnus-completing-read "NNTP server" + (cons gnus-nntp-server + (if (boundp 'gnus-secondary-servers) + gnus-secondary-servers)) + nil gnus-nntp-server))) + (when (and (stringp gnus-nntp-server) + (not (zerop (length gnus-nntp-server)))) + ;; this will also assign the obsolete variable `gnus-select-method' + (custom-set-variables + `(gnus-select-methods + (quote (,(cond ((string= gnus-nntp-server "::") + (list 'nnspool (system-name))) + ((string-match "^:" gnus-nntp-server) + (list 'nnmh gnus-nntp-server + (list 'nnmh-directory + (file-name-as-directory + (expand-file-name + (substring gnus-nntp-server 1) "~/"))) + (list 'nnmh-get-new-mail nil))) + (t + (list 'nntp gnus-nntp-server)))))))) + (setq gnus-current-select-method gnus-select-method) + (cl-case (car gnus-select-method) + ('nnspool + (require 'nnspool) + (gnus-message 5 "Looking up local news spool...")) + ('nnmh + (require 'nnmh) + (gnus-message 5 "Looking up mh spool...")) + (t + (require 'nntp))) + (gnus-run-hooks 'gnus-open-server-hook) + + ;; Partially validate agent covered methods now that the + ;; gnus-select-method is known. + + (if gnus-agent + ;; NOTE: This is here for one purpose only. By validating + ;; the current select method, it converts the old 5.10.3, + ;; and earlier, format to the current format. That enables + ;; the agent code within gnus-open-server to function + ;; correctly. + (gnus-agent-read-servers-validate-native gnus-select-method)) - ;; Partially validate agent covered methods now that the - ;; gnus-select-method is known. - - (if gnus-agent - ;; NOTE: This is here for one purpose only. By validating - ;; the current select method, it converts the old 5.10.3, - ;; and earlier, format to the current format. That enables - ;; the agent code within gnus-open-server to function - ;; correctly. - (gnus-agent-read-servers-validate-native gnus-select-method)) - - (or - ;; gnus-open-server-hook might have opened it - (gnus-server-opened gnus-select-method) - (gnus-open-server gnus-select-method) - gnus-batch-mode - (gnus-y-or-n-p - (format-message - "%s (%s) open error: `%s'. Continue? " - (car gnus-select-method) (cadr gnus-select-method) - (gnus-status-message gnus-select-method))) - (gnus-error 1 "Couldn't open server on %s" - (nth 1 gnus-select-method)))))) + (or + ;; gnus-open-server-hook might have opened it + (gnus-server-opened gnus-select-method) + (gnus-open-server gnus-select-method) + gnus-batch-mode + (gnus-y-or-n-p + (format-message + "%s (%s) open error%s. Continue? " + (car gnus-select-method) (cadr gnus-select-method) + (if-let ((status (gnus-status-message gnus-select-method)) + (status* (not (zerop (length status))))) + (format ": `%s'" status) + ""))) + (gnus-error 1 "Couldn't open server on %s" + (nth 1 gnus-select-method))))) (defun gnus-check-group (group) "Try to make sure that the server where GROUP exists is alive." @@ -219,7 +220,7 @@ gnus-get-function (setq method (gnus-server-to-method method))) ;; Check cache of constructed names. (let* ((method-sym (if gnus-agent - (inline (gnus-agent-get-function method)) + (gnus-agent-get-function method) (car method))) (method-fns (get method-sym 'gnus-method-functions)) (func (let ((method-fnlist-elt (assq function method-fns))) @@ -488,11 +489,11 @@ gnus-request-compact (defun gnus-request-group (group &optional dont-check command-method info) "Request GROUP. If DONT-CHECK, no information is required." (let ((gnus-command-method - (or command-method (inline (gnus-find-method-for-group group))))) + (or command-method (gnus-find-method-for-group group)))) (when (stringp gnus-command-method) (setq gnus-command-method - (inline (gnus-server-to-method gnus-command-method)))) - (funcall (inline (gnus-get-function gnus-command-method 'request-group)) + (gnus-server-to-method gnus-command-method))) + (funcall (gnus-get-function gnus-command-method 'request-group) (gnus-group-real-name group) (nth 1 gnus-command-method) dont-check info))) @@ -515,7 +516,7 @@ gnus-request-group-scan (defun gnus-close-group (group) "Request the GROUP be closed." - (let ((gnus-command-method (inline (gnus-find-method-for-group group)))) + (let ((gnus-command-method (gnus-find-method-for-group group))) (funcall (gnus-get-function gnus-command-method 'close-group) (gnus-group-real-name group) (nth 1 gnus-command-method)))) diff --git a/lisp/gnus/gnus-kill.el b/lisp/gnus/gnus-kill.el index f73627a648..4a480dde66 100644 --- a/lisp/gnus/gnus-kill.el +++ b/lisp/gnus/gnus-kill.el @@ -333,60 +333,48 @@ gnus-apply-kill-file-unless-scored (defun gnus-apply-kill-file-internal () "Apply a kill file to the current newsgroup. Returns the number of articles marked as read." - (let* ((kill-files (list (gnus-newsgroup-kill-file nil) - (gnus-newsgroup-kill-file gnus-newsgroup-name))) - (unreads (length gnus-newsgroup-unreads)) - (gnus-summary-inhibit-highlight t) - ) ;; beg - (setq gnus-newsgroup-kill-headers nil) - ;; If there are any previously scored articles, we remove these - ;; from the `gnus-newsgroup-headers' list that the score functions - ;; will see. This is probably pretty wasteful when it comes to - ;; conses, but is, I think, faster than having to assq in every - ;; single score function. - (let ((files kill-files)) - (while files - (if (file-exists-p (car files)) - (let ((headers gnus-newsgroup-headers)) - (if gnus-kill-killed - (setq gnus-newsgroup-kill-headers - (mapcar #'mail-header-number headers)) - (while headers - (unless (gnus-member-of-range - (mail-header-number (car headers)) - gnus-newsgroup-killed) - (push (mail-header-number (car headers)) - gnus-newsgroup-kill-headers)) - (setq headers (cdr headers)))) - (setq files nil)) - (setq files (cdr files))))) - (if (not gnus-newsgroup-kill-headers) - () - (save-window-excursion - (save-excursion - (while kill-files - (if (not (file-exists-p (car kill-files))) - () - (gnus-message 6 "Processing kill file %s..." (car kill-files)) - (find-file (car kill-files)) - (goto-char (point-min)) - - (if (consp (ignore-errors (read (current-buffer)))) - (gnus-kill-parse-gnus-kill-file) - (gnus-kill-parse-rn-kill-file)) - - (gnus-message - 6 "Processing kill file %s...done" (car kill-files))) - (setq kill-files (cdr kill-files))))) - - (gnus-set-mode-line 'summary) - - (if nil ;; beg - (let ((nunreads (- unreads (length gnus-newsgroup-unreads)))) - (or (eq nunreads 0) - (gnus-message 6 "Marked %d articles as read" nunreads)) - nunreads) - 0)))) + (prog1 0 + (let ((kill-files (list (gnus-newsgroup-kill-file nil) + (gnus-newsgroup-kill-file gnus-newsgroup-name))) + (gnus-summary-inhibit-highlight t)) + (setq gnus-newsgroup-kill-headers nil) + ;; If there are any previously scored articles, we remove these + ;; from the `gnus-newsgroup-headers' list that the score functions + ;; will see. This is probably pretty wasteful when it comes to + ;; conses, but is, I think, faster than having to assq in every + ;; single score function. + (let ((files kill-files)) + (while files + (if (file-exists-p (car files)) + (let ((headers gnus-newsgroup-headers)) + (if gnus-kill-killed + (setq gnus-newsgroup-kill-headers + (mapcar #'mail-header-number headers)) + (while headers + (unless (gnus-member-of-range + (mail-header-number (car headers)) + gnus-newsgroup-killed) + (push (mail-header-number (car headers)) + gnus-newsgroup-kill-headers)) + (setq headers (cdr headers)))) + (setq files nil)) + (setq files (cdr files))))) + (unless gnus-newsgroup-kill-headers + (save-window-excursion + (save-excursion + (dolist (file kill-files) + (unless (file-exists-p file) + (gnus-message 6 "Processing kill file %s..." file) + (when-let ((buf (find-file-noselect file))) + (unwind-protect + (with-current-buffer buf + (if (consp (ignore-errors (read (current-buffer)))) + (gnus-kill-parse-gnus-kill-file) + (gnus-kill-parse-rn-kill-file)) + (gnus-message 6 "Processing kill file %s...done" file)) + (let (kill-buffer-query-functions) + (kill-buffer buf)))))))) + (gnus-set-mode-line 'summary))))) ;; Parse a Gnus killfile. (defun gnus-kill-parse-gnus-kill-file () diff --git a/lisp/gnus/gnus-logic.el b/lisp/gnus/gnus-logic.el index cdfdc9b731..261cabfb04 100644 --- a/lisp/gnus/gnus-logic.el +++ b/lisp/gnus/gnus-logic.el @@ -24,11 +24,8 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'gnus) (require 'gnus-score) -(require 'gnus-util) ;;; Internal variables. diff --git a/lisp/gnus/gnus-mlspl.el b/lisp/gnus/gnus-mlspl.el index d42f097125..34840b1906 100644 --- a/lisp/gnus/gnus-mlspl.el +++ b/lisp/gnus/gnus-mlspl.el @@ -70,7 +70,7 @@ gnus-group-split-setup ;; Split updating requires `gnus-newsrc-hashtb' to be ;; initialized; the read newsrc hook is the only hook that comes ;; after initialization, but before checking for new news. - 'gnus-read-newsrc-el-hook) + 'gnus-read-newsrc-hook) #'gnus-group-split-update)) ;;;###autoload diff --git a/lisp/gnus/gnus-msg.el b/lisp/gnus/gnus-msg.el index bac987e2f0..87c775a44a 100644 --- a/lisp/gnus/gnus-msg.el +++ b/lisp/gnus/gnus-msg.el @@ -25,12 +25,9 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - -(require 'gnus) (require 'message) (require 'gnus-art) -(require 'gnus-util) +(require 'cl-extra) (defcustom gnus-post-method 'current "Preferred method for posting USENET news. @@ -398,90 +395,83 @@ gnus-inews-make-draft (defvar gnus-article-reply nil) (defmacro gnus-setup-message (config &rest forms) (declare (indent 1) (debug t)) - (let ((winconf (make-symbol "gnus-setup-message-winconf")) - (winconf-name (make-symbol "gnus-setup-message-winconf-name")) - (buffer (make-symbol "gnus-setup-message-buffer")) - (article (make-symbol "gnus-setup-message-article")) - (oarticle (make-symbol "gnus-setup-message-oarticle")) - (yanked (make-symbol "gnus-setup-yanked-articles")) - (group (make-symbol "gnus-setup-message-group"))) - `(let ((,winconf (current-window-configuration)) - (,winconf-name gnus-current-window-configuration) - (,buffer (buffer-name (current-buffer))) - (,article (when gnus-article-reply - (or (nnselect-article-number - (or (car-safe gnus-article-reply) - gnus-article-reply)) - gnus-article-reply))) - (,oarticle gnus-article-reply) - (,yanked gnus-article-yanked-articles) - (,group (if gnus-article-reply - (or (nnselect-article-group - (or (car-safe gnus-article-reply) - gnus-article-reply)) - gnus-newsgroup-name) - gnus-newsgroup-name)) - (message-header-setup-hook - (copy-sequence message-header-setup-hook)) - (mbl mml-buffer-list) - (message-mode-hook (copy-sequence message-mode-hook))) - (setq mml-buffer-list nil) - (add-hook 'message-header-setup-hook (lambda () - (gnus-inews-insert-gcc ,group))) - ;; message-newsreader and message-mailer were formerly set in - ;; gnus-inews-add-send-actions, but this is too late when - ;; message-generate-headers-first is used. --ansel - (add-hook 'message-mode-hook - (lambda nil - (setq message-newsreader - (setq message-mailer (gnus-extended-version))))) - ;; #### FIXME: for a reason that I did not manage to identify yet, - ;; the variable `gnus-newsgroup-name' does not honor a dynamically - ;; scoped or setq'ed value from a caller like `C-u gnus-summary-mail'. - ;; After evaluation of @forms below, it gets the value we actually want - ;; to override, and the posting styles are used. For that reason, I've - ;; added an optional argument to `gnus-configure-posting-styles' to - ;; make sure that the correct value for the group name is used. -- drv - (add-hook 'message-mode-hook - (if (memq ,config '(reply-yank reply)) - (lambda () - (gnus-configure-posting-styles ,group)) + `(let ((setup-winconf (current-window-configuration)) + (setup-winconf-name gnus-current-window-configuration) + (setup-buffer (buffer-name (current-buffer))) + (setup-article (when gnus-article-reply + (or (nnselect-article-number + (or (car-safe gnus-article-reply) + gnus-article-reply)) + gnus-article-reply))) + (setup-oarticle gnus-article-reply) + (setup-yanked gnus-article-yanked-articles) + (setup-group (if gnus-article-reply + (or (nnselect-article-group + (or (car-safe gnus-article-reply) + gnus-article-reply)) + gnus-newsgroup-name) + gnus-newsgroup-name)) + (message-header-setup-hook + (copy-sequence message-header-setup-hook)) + (mbl mml-buffer-list) + (message-mode-hook (copy-sequence message-mode-hook))) + (setq mml-buffer-list nil) + (add-hook 'message-header-setup-hook (lambda () + (gnus-inews-insert-gcc setup-group))) + ;; message-newsreader and message-mailer were formerly set in + ;; gnus-inews-add-send-actions, but this is too late when + ;; message-generate-headers-first is used. --ansel + (add-hook 'message-mode-hook + (lambda nil + (setq message-newsreader + (setq message-mailer (gnus-extended-version))))) + ;; #### FIXME: for a reason that I did not manage to identify yet, + ;; the variable `gnus-newsgroup-name' does not honor a dynamically + ;; scoped or setq'ed value from a caller like `C-u gnus-summary-mail'. + ;; After evaluation of @forms below, it gets the value we actually want + ;; to override, and the posting styles are used. For that reason, I've + ;; added an optional argument to `gnus-configure-posting-styles' to + ;; make sure that the correct value for the group name is used. -- drv + (add-hook 'message-mode-hook + (if (memq ,config '(reply-yank reply)) (lambda () - ;; There may be an old " *gnus article copy*" buffer. - (let (gnus-article-copy) - (gnus-configure-posting-styles ,group))))) - (gnus-alist-pull ',(intern gnus-draft-meta-information-header) - message-required-headers) - (when (and ,group - (not (string= ,group ""))) - (push (cons - (intern gnus-draft-meta-information-header) - (gnus-inews-make-draft (or ,yanked ,article))) - message-required-headers)) - (unwind-protect - (progn - ,@forms) - (gnus-inews-add-send-actions ,winconf ,buffer ,oarticle ,config - ,yanked ,winconf-name) - (setq gnus-message-buffer (current-buffer)) - (setq-local gnus-message-group-art (cons ,group ,article)) - ;; Enable highlighting of different citation levels - (when gnus-message-highlight-citation - (gnus-message-citation-mode 1)) - (gnus-run-hooks 'gnus-message-setup-hook) - (if (eq major-mode 'message-mode) - (let ((mbl1 mml-buffer-list)) - (setq mml-buffer-list mbl) ;; Global value - (setq-local mml-buffer-list mbl1) ;; Local value - (add-hook 'change-major-mode-hook #'mml-destroy-buffers nil t) - (add-hook 'kill-buffer-hook #'mml-destroy-buffers t t)) - (mml-destroy-buffers) - (setq mml-buffer-list mbl))) - (message-hide-headers) - (gnus-add-buffer) - (gnus-configure-windows ,config t) - (run-hooks 'post-command-hook) - (set-buffer-modified-p nil)))) + (gnus-configure-posting-styles setup-group)) + (lambda () + ;; There may be an old " *gnus article copy*" buffer. + (let (gnus-article-copy) + (gnus-configure-posting-styles setup-group))))) + (gnus-alist-pull ',(intern gnus-draft-meta-information-header) + message-required-headers) + (when (and setup-group + (not (string= setup-group ""))) + (push (cons + (intern gnus-draft-meta-information-header) + (gnus-inews-make-draft (or setup-yanked setup-article))) + message-required-headers)) + (unwind-protect + (progn + ,@forms) + (gnus-inews-add-send-actions setup-winconf setup-buffer setup-oarticle ,config + setup-yanked setup-winconf-name) + (setq gnus-message-buffer (current-buffer)) + (setq-local gnus-message-group-art (cons setup-group setup-article)) + ;; Enable highlighting of different citation levels + (when gnus-message-highlight-citation + (gnus-message-citation-mode 1)) + (gnus-run-hooks 'gnus-message-setup-hook) + (if (eq major-mode 'message-mode) + (let ((mbl1 mml-buffer-list)) + (setq mml-buffer-list mbl) ;; Global value + (setq-local mml-buffer-list mbl1) ;; Local value + (add-hook 'change-major-mode-hook #'mml-destroy-buffers nil t) + (add-hook 'kill-buffer-hook #'mml-destroy-buffers t t)) + (mml-destroy-buffers) + (setq mml-buffer-list mbl))) + (message-hide-headers) + (gnus-add-buffer) + (gnus-configure-windows ,config t) + (run-hooks 'post-command-hook) + (set-buffer-modified-p nil))) (defun gnus-inews-make-draft-meta-information (group articles) (when (numberp articles) @@ -577,8 +567,8 @@ gnus-inews-add-send-actions (when gnus-agent (add-hook 'message-header-hook #'gnus-agent-possibly-save-gcc nil t)) (setq message-post-method - (let ((gn gnus-newsgroup-name)) - (lambda (&optional arg) (gnus-post-method arg gn)))) + `(lambda (&optional arg) + (gnus-post-method arg ,(buffer-local-value 'gnus-newsgroup-name (get-buffer buffer))))) (message-add-action `(progn (setq gnus-current-window-configuration ',winconf-name) @@ -661,7 +651,7 @@ gnus-group-post-news (or (gnus-group-group-name) "")) "")) ;; make sure last viewed article doesn't affect posting styles: - (gnus-article-copy)) + gnus-article-copy) (gnus-post-news 'post gnus-newsgroup-name nil nil nil nil (string= gnus-newsgroup-name "")))) @@ -869,58 +859,85 @@ gnus-copy-article-buffer (mm-enable-multibyte)) (let ((article-buffer (or article-buffer gnus-article-buffer)) end beg) - (if (not (gnus-buffer-live-p article-buffer)) - (error "Can't find any article buffer") - (with-current-buffer article-buffer - (let ((gnus-newsgroup-charset (or gnus-article-charset - gnus-newsgroup-charset)) - (inhibit-read-only t) - (gnus-newsgroup-ignored-charsets - (or gnus-article-ignored-charsets - gnus-newsgroup-ignored-charsets))) - (save-restriction - ;; Copy over the (displayed) article buffer, delete - ;; hidden text and remove text properties. - (widen) - (copy-to-buffer gnus-article-copy (point-min) (point-max)) - (set-buffer gnus-article-copy) - (when yank-string - (message-goto-body) - (delete-region (point) (point-max)) - (insert yank-string)) - (gnus-article-delete-text-of-type 'annotation) - (gnus-article-delete-text-of-type 'multipart) - (gnus-remove-text-with-property 'gnus-prev) - (gnus-remove-text-with-property 'gnus-next) - (gnus-remove-text-with-property 'gnus-decoration) - (insert - (prog1 - (buffer-substring-no-properties (point-min) (point-max)) - (erase-buffer))) - ;; Find the original headers. - (set-buffer gnus-original-article-buffer) - (goto-char (point-min)) - (while (looking-at message-unix-mail-delimiter) - (forward-line 1)) - (let ((mail-header-separator "")) - (setq beg (point) - end (or (message-goto-body) - ;; There may be just a header. - (point-max)))) - ;; Delete the headers from the displayed articles. - (set-buffer gnus-article-copy) - (let ((mail-header-separator "")) - (delete-region (goto-char (point-min)) - (or (message-goto-body) (point-max)))) - ;; Insert the original article headers. - (insert-buffer-substring gnus-original-article-buffer beg end) - ;; Decode charsets. - (let ((gnus-article-decode-hook - (delq 'article-decode-charset - (copy-sequence gnus-article-decode-hook))) - (rfc2047-quote-decoded-words-containing-tspecials t)) - (run-hooks 'gnus-article-decode-hook))))) - gnus-article-copy))) + (unless (gnus-buffer-live-p article-buffer) + (error "Can't find any article buffer")) + (gnus-msg-inherit-variables (get-buffer article-buffer) gnus-article-copy) + (with-current-buffer article-buffer + (let ((gnus-newsgroup-charset (or gnus-article-charset + gnus-newsgroup-charset)) + (inhibit-read-only t) + (gnus-newsgroup-ignored-charsets + (or gnus-article-ignored-charsets + gnus-newsgroup-ignored-charsets))) + (save-restriction + ;; Copy over the (displayed) article buffer, delete + ;; hidden text and remove text properties. + (widen) + (copy-to-buffer gnus-article-copy (point-min) (point-max)) + (with-current-buffer gnus-article-copy + (when yank-string + (message-goto-body) + (delete-region (point) (point-max)) + (insert yank-string)) + (gnus-article-delete-text-of-type 'annotation) + (gnus-article-delete-text-of-type 'multipart) + (gnus-remove-text-with-property 'gnus-prev) + (gnus-remove-text-with-property 'gnus-next) + (gnus-remove-text-with-property 'gnus-decoration) + (insert + (prog1 + (buffer-substring-no-properties (point-min) (point-max)) + (erase-buffer)))) + ;; Find the original headers. + (with-current-buffer gnus-original-article-buffer + (goto-char (point-min)) + (while (looking-at message-unix-mail-delimiter) + (forward-line 1)) + (let ((mail-header-separator "")) + (setq beg (point) + end (or (message-goto-body) + ;; There may be just a header. + (point-max))))) + ;; Delete the headers from the displayed articles. + (with-current-buffer gnus-article-copy + (let ((mail-header-separator "")) + (delete-region (goto-char (point-min)) + (or (message-goto-body) (point-max)))) + ;; Insert the original article headers. + (insert-buffer-substring gnus-original-article-buffer beg end) + ;; Decode charsets. + (let ((gnus-article-decode-hook + (delq 'article-decode-charset + (copy-sequence gnus-article-decode-hook))) + (rfc2047-quote-decoded-words-containing-tspecials t)) + (run-hooks 'gnus-article-decode-hook)))))) + gnus-article-copy)) + +(defmacro gnus-msg-preserve-variables (parent-buffer &rest body) + "If BODY changes the current buffer, ensure important variables preserved." + (declare (indent 1)) + `(progn + ,@body + (unless (eq ,parent-buffer (current-buffer)) + (gnus-msg-inherit-variables ,parent-buffer (current-buffer))))) + +(defun gnus-msg-inherit-variables (source-buffer dest-buffer) + "Transfer formerly global variables from SOURCE-BUFFER to DEST-BUFFER." + (with-current-buffer dest-buffer + (mapc (lambda (pair) + (set (make-local-variable (car pair)) + (cdr pair))) + (cl-mapcan (lambda (variable) + (when (local-variable-if-set-p variable) + (list `(,variable . ,(buffer-local-value + variable + source-buffer))))) + '(gnus-summary-buffer + gnus-article-buffer + gnus-original-article-buffer + gnus-newsgroup-name + gnus-article-current + gnus-current-article))))) (defun gnus-post-news (post &optional group header article-buffer yank _subject force-news) @@ -963,13 +980,15 @@ gnus-post-news (and (not (gnus-virtual-group-p pgroup)) group))) (set-buffer gnus-article-copy) (gnus-msg-treat-broken-reply-to) - (message-followup (if (or newsgroup-p force-news) - (if (save-restriction - (article-narrow-to-head) - (message-fetch-field "newsgroups")) - nil - "") - to-group))) + (let ((parent-buffer (current-buffer))) + (gnus-msg-preserve-variables parent-buffer + (message-followup (if (or newsgroup-p force-news) + (if (save-restriction + (article-narrow-to-head) + (message-fetch-field "newsgroups")) + nil + "") + to-group))))) ;; The is mail. (if post (progn @@ -983,7 +1002,9 @@ gnus-post-news message-send-actions))) (set-buffer gnus-article-copy) (gnus-msg-treat-broken-reply-to) - (message-wide-reply to-address))) + (let ((parent-buffer (current-buffer))) + (gnus-msg-preserve-variables parent-buffer + (message-wide-reply to-address))))) (when yank (gnus-inews-yank-articles yank)))))) @@ -1027,10 +1048,9 @@ gnus-post-method (if (listp (car gnus-post-method)) gnus-post-method (list gnus-post-method))) - gnus-secondary-select-methods + gnus-select-methods (mapcar #'cdr gnus-server-alist) (mapcar #'car gnus-opened-servers) - (list gnus-select-method) (list group-method))) method-alist post-methods method) ;; Weed out all mail methods. @@ -1105,10 +1125,11 @@ gnus-summary-reply If prefix argument YANK is non-nil, the original article is yanked automatically. If WIDE, make a wide reply. -If VERY-WIDE, make a very wide reply." - (interactive (list (and current-prefix-arg - (gnus-summary-work-articles 1))) - gnus-summary-mode) +VERY-WIDE is a list of other articles to reply to." + (interactive + (list (and current-prefix-arg + (gnus-summary-work-articles 1))) + gnus-summary-mode) ;; Allow user to require confirmation before replying by mail to the ;; author of a news article (or mail message). (when (or (not (or (gnus-news-group-p gnus-newsgroup-name) @@ -1120,40 +1141,38 @@ gnus-summary-reply (funcall gnus-confirm-mail-reply-to-news gnus-newsgroup-name)) (t gnus-confirm-mail-reply-to-news))) - (if (or wide very-wide) - t ;; Ignore gnus-confirm-mail-reply-to-news for wide and very - ;; wide replies. - (y-or-n-p "Really reply by mail to article author? "))) - (let* ((article - (if (listp (car yank)) - (caar yank) - (car yank))) + (or wide very-wide + (y-or-n-p "Really reply by mail to article author? "))) + (let* ((article (if (listp (car yank)) (caar yank) (car yank))) (gnus-article-reply (or article (gnus-summary-article-number))) - (gnus-article-yanked-articles yank) - (headers "")) + (gnus-article-yanked-articles yank)) ;; Stripping headers should be specified with mail-yank-ignored-headers. (when yank (gnus-summary-goto-subject article)) (gnus-setup-message (if yank 'reply-yank 'reply) - (if (not very-wide) - (gnus-summary-select-article) - (dolist (article very-wide) - (gnus-summary-select-article nil nil nil article) - (with-current-buffer (gnus-copy-article-buffer) - (gnus-msg-treat-broken-reply-to) - (save-restriction - (message-narrow-to-head) - (setq headers (concat headers (buffer-string))))))) + (gnus-summary-select-article nil nil nil gnus-article-reply) (set-buffer (gnus-copy-article-buffer)) (gnus-msg-treat-broken-reply-to gnus-msg-force-broken-reply-to) - (save-restriction - (message-narrow-to-head) - (when very-wide - (erase-buffer) - (insert headers)) - (goto-char (point-max))) - (mml-quote-region (point) (point-max)) - (message-reply nil wide) + (save-restriction + (message-narrow-to-head) + (when-let ((very-wide-headers + (save-current-buffer + (let (result) + (dolist (art very-wide result) + (gnus-summary-select-article nil nil nil art) + (with-current-buffer (gnus-copy-article-buffer) + (gnus-msg-treat-broken-reply-to) + (save-restriction + (message-narrow-to-head) + (setq result (concat (or result "") + (buffer-string)))))))))) + (erase-buffer) + (insert very-wide-headers)) + (goto-char (point-max))) + (let ((parent-buffer (current-buffer))) + (gnus-msg-preserve-variables parent-buffer + (mml-quote-region (point) (point-max)) + (message-reply nil wide))) (when yank (gnus-inews-yank-articles yank)) (gnus-summary-handle-replysign))))) diff --git a/lisp/gnus/gnus-notifications.el b/lisp/gnus/gnus-notifications.el index 8646904637..aeb590c28c 100644 --- a/lisp/gnus/gnus-notifications.el +++ b/lisp/gnus/gnus-notifications.el @@ -35,7 +35,6 @@ (require 'gnus-group) (require 'gnus-int) (require 'gnus-art) -(require 'gnus-util) (ignore-errors (require 'google-contacts)) ; Optional (require 'gnus-fun) diff --git a/lisp/gnus/gnus-picon.el b/lisp/gnus/gnus-picon.el index fd4d3b8a76..a65521b458 100644 --- a/lisp/gnus/gnus-picon.el +++ b/lisp/gnus/gnus-picon.el @@ -37,8 +37,6 @@ ;; ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'gnus) (require 'gnus-art) diff --git a/lisp/gnus/gnus-registry.el b/lisp/gnus/gnus-registry.el index 0468d72edd..1bca887889 100644 --- a/lisp/gnus/gnus-registry.el +++ b/lisp/gnus/gnus-registry.el @@ -79,16 +79,14 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) -(eval-when-compile (require 'subr-x)) (require 'gnus) (require 'gnus-int) (require 'gnus-sum) (require 'gnus-art) -(require 'gnus-util) (require 'nnmail) (require 'registry) +(require 'subr-x) (defvar gnus-adaptive-word-syntax-table) @@ -1187,7 +1185,7 @@ gnus-registry-initialize (gnus-registry-install-shortcuts) (if (gnus-alive-p) (gnus-registry-load) - (add-hook 'gnus-read-newsrc-el-hook #'gnus-registry-load))) + (add-hook 'gnus-read-newsrc-hook #'gnus-registry-load))) (defun gnus-registry-install-hooks () "Install the registry hooks." @@ -1209,7 +1207,7 @@ gnus-registry-unload-hook (remove-hook 'nnmail-spool-hook #'gnus-registry-spool-action) (remove-hook 'gnus-save-newsrc-hook #'gnus-registry-save) - (remove-hook 'gnus-read-newsrc-el-hook #'gnus-registry-load) + (remove-hook 'gnus-read-newsrc-hook #'gnus-registry-load) (remove-hook 'gnus-summary-prepare-hook #'gnus-registry-register-message-ids) (setq gnus-registry-enabled nil)) diff --git a/lisp/gnus/gnus-salt.el b/lisp/gnus/gnus-salt.el index 5b746a8efa..6fb1282a1e 100644 --- a/lisp/gnus/gnus-salt.el +++ b/lisp/gnus/gnus-salt.el @@ -24,8 +24,6 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'gnus) (require 'gnus-sum) (require 'gnus-win) @@ -110,7 +108,7 @@ gnus-pick-mode ;; Change line format. (setq gnus-summary-line-format gnus-summary-pick-line-format) (setq gnus-summary-line-format-spec nil) - (gnus-update-format-specifications nil 'summary) + (gnus-update-format-specifications 'summary) (gnus-update-summary-mark-positions) ;; FIXME: a buffer-local minor mode adding globally to a hook?? (add-hook 'gnus-message-setup-hook #'gnus-pick-setup-message) @@ -457,8 +455,7 @@ gnus-tree-mode (buffer-disable-undo) (setq buffer-read-only t) (setq truncate-lines t) - (save-current-buffer - (gnus-set-work-buffer) + (gnus-with-temp-buffer (gnus-tree-node-insert (make-mail-header "") nil) (setq gnus-tree-node-length (1- (point))))) diff --git a/lisp/gnus/gnus-score.el b/lisp/gnus/gnus-score.el index f40da9e9c4..3e71bb56c4 100644 --- a/lisp/gnus/gnus-score.el +++ b/lisp/gnus/gnus-score.el @@ -460,7 +460,7 @@ gnus-adaptive-word-score-alist (defvar gnus-score-trace nil) (defvar gnus-score-edit-buffer nil) -(defvar gnus-score-alist nil +(defvar-local gnus-score-alist nil "Alist containing score information. The keys can be symbols or strings. The following symbols are defined. @@ -1444,24 +1444,21 @@ gnus-score-save ;; Save all score information. (let ((cache gnus-score-cache) entry score file) - (save-excursion + (with-temp-buffer (setq gnus-score-alist nil) - (nnheader-set-temp-buffer " *Gnus Scores*") (while cache - (current-buffer) (setq entry (pop cache) file (nnheader-translate-file-chars (car entry) t) score (cdr entry)) - (if (or (not (equal (gnus-score-get 'touched score) '(t))) - (gnus-score-get 'read-only score) - (and (file-exists-p file) - (not (file-writable-p file)))) - () + (when (and (equal (gnus-score-get 'touched score) '(t)) + (not (gnus-score-get 'read-only score)) + (or (not (file-exists-p file)) + (file-writable-p file))) (setq score (setcdr entry (assq-delete-all 'touched score))) (erase-buffer) (let (emacs-lisp-mode-hook) (if (and (not gnus-adaptive-pretty-print) - (string-match + (string-match-p (concat (regexp-quote gnus-adaptive-file-suffix) "$") file)) ;; This is an adaptive score file, so we do not run it through @@ -1482,10 +1479,9 @@ gnus-score-save (gnus-write-buffer file)) (when gnus-score-after-write-file-function (funcall gnus-score-after-write-file-function file))))) - (and gnus-score-uncacheable-files - (string-match gnus-score-uncacheable-files file) - (gnus-score-remove-from-cache file))) - (kill-buffer (current-buffer))))) + (when (and gnus-score-uncacheable-files + (string-match-p gnus-score-uncacheable-files file)) + (gnus-score-remove-from-cache file)))))) (defun gnus-score-load-files (score-files) "Load all score files in SCORE-FILES." @@ -1524,8 +1520,7 @@ gnus-score-headers (while news (setq scores news news nil) - (when (and gnus-summary-default-score - scores) + (when (and gnus-summary-default-score scores) (let* ((entries gnus-header-index) (now (time-to-days nil)) (expire (and gnus-score-expiry-days @@ -1544,8 +1539,7 @@ gnus-score-headers (cons (cons header (or gnus-summary-default-score 0)) gnus-scores-articles)))) - (with-current-buffer (gnus-get-buffer-create "*Headers*") - (buffer-disable-undo) + (with-temp-buffer (when (gnus-buffer-live-p gnus-summary-buffer) (message-clone-locals gnus-summary-buffer)) @@ -1589,9 +1583,7 @@ gnus-score-headers (when (gnus-buffer-live-p gnus-summary-buffer) (let ((scored gnus-newsgroup-scored)) (with-current-buffer gnus-summary-buffer - (setq gnus-newsgroup-scored scored)))) - ;; Remove the buffer. - (gnus-kill-buffer (current-buffer))) + (setq gnus-newsgroup-scored scored))))) ;; Add articles to `gnus-newsgroup-scored'. (while gnus-scores-articles @@ -2916,7 +2908,7 @@ gnus-sort-score-files (let ((alist (mapcar (lambda (file) - (cons (inline (gnus-score-file-rank file)) file)) + (cons (gnus-score-file-rank file) file)) files))) (mapcar #'cdr (sort alist #'car-less-than-car))))) diff --git a/lisp/gnus/gnus-search.el b/lisp/gnus/gnus-search.el index fc9f8684f6..91f2decaf8 100644 --- a/lisp/gnus/gnus-search.el +++ b/lisp/gnus/gnus-search.el @@ -81,9 +81,8 @@ (require 'gnus-group) (require 'gnus-sum) (require 'message) -(require 'gnus-util) (require 'eieio) -(eval-when-compile (require 'cl-lib)) + (autoload 'eieio-build-class-alist "eieio-opt") (autoload 'nnmaildir-base-name-to-article-number "nnmaildir") @@ -1024,7 +1023,7 @@ gnus-search-run-search ;; We should only be doing this once, in ;; `nnimap-open-connection', but it's too frustrating to try to ;; get to the server from the process buffer. - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (setf (slot-value engine 'literal-plus) (when (nnimap-capability "LITERAL+") t)) ;; MULTISEARCH not yet implemented. @@ -1062,7 +1061,7 @@ gnus-search-run-search (or (null single-search) (null artlist))) (when (nnimap-change-group (gnus-group-short-name group) server) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (message "Searching %s..." group) (let ((result (gnus-search-imap-search-command engine q-string))) diff --git a/lisp/gnus/gnus-spec.el b/lisp/gnus/gnus-spec.el index cb60108ea9..ec7c7e7406 100644 --- a/lisp/gnus/gnus-spec.el +++ b/lisp/gnus/gnus-spec.el @@ -24,14 +24,12 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) -(defvar gnus-newsrc-file-version) - (require 'gnus) ;;; Internal variables. -(defvar gnus-summary-mark-positions nil) +(defvar gnus-newsrc-file-version) +(defvar-local gnus-summary-mark-positions nil) (defvar gnus-group-mark-positions nil) (defvar gnus-group-indentation "") @@ -129,13 +127,12 @@ gnus-update-format (lisp-interaction-mode) (insert (gnus-pp-to-string spec)))) -(defun gnus-update-format-specifications (&optional force &rest types) +(defun gnus-update-format-specifications (&rest types) "Update all (necessary) format specifications. Return a list of updated types." ;; Make the indentation array. ;; See whether all the stored info needs to be flushed. - (when (or force - (not gnus-newsrc-file-version) + (when (or (not gnus-newsrc-file-version) (not (equal (gnus-continuum-version) (gnus-continuum-version gnus-newsrc-file-version))) (not (equal emacs-version @@ -374,56 +371,55 @@ gnus-parse-format (if (string-match "\\`\\(.*\\)%[0-9]?[{(«]\\(.*\\)%[0-9]?[»})]\\(.*\n?\\)\\'\\|%[-0-9]*=\\|%[-0-9]*\\*" format) - (gnus-parse-complex-format format spec-alist) + (gnus-parse-complex-format format spec-alist) ;; This is a simple format. (gnus-parse-simple-format format spec-alist insert)))) (defun gnus-parse-complex-format (format spec-alist) - (let ((cursor-spec nil)) - (save-excursion - (gnus-set-work-buffer) - (insert format) - (goto-char (point-min)) - (while (re-search-forward "\"" nil t) - (replace-match "\\\"" nil t)) - (goto-char (point-min)) - (insert "(\"") - ;; Convert all font specs into font spec lists. - (while (re-search-forward "%\\([0-9]+\\)?\\([«»{}()]\\)" nil t) - (let ((number (if (match-beginning 1) - (match-string 1) "0")) - (delim (aref (match-string 2) 0))) - (if (or (= delim ?\() - (= delim ?\{) - (= delim 171)) ; « - (replace-match (concat "\"(" - (cond ((= delim ?\() "mouse") - ((= delim ?\{) "face") - (t "balloon")) - " " number " \"") - t t) - (replace-match "\")\"")))) - (goto-char (point-max)) - (insert "\")") - ;; Convert point position commands. - (goto-char (point-min)) + (gnus-with-temp-buffer + (insert format) + (goto-char (point-min)) + (while (re-search-forward "\"" nil t) + (replace-match "\\\"" nil t)) + (goto-char (point-min)) + (insert "(\"") + ;; Convert all font specs into font spec lists. + (while (re-search-forward "%\\([0-9]+\\)?\\([«»{}()]\\)" nil t) + (let ((number (if (match-beginning 1) + (match-string 1) "0")) + (delim (aref (match-string 2) 0))) + (if (or (= delim ?\() + (= delim ?\{) + (= delim 171)) ; « + (replace-match (concat "\"(" + (cond ((= delim ?\() "mouse") + ((= delim ?\{) "face") + (t "balloon")) + " " number " \"") + t t) + (replace-match "\")\"")))) + (goto-char (point-max)) + (insert "\")") + ;; Convert point position commands. + (goto-char (point-min)) + (let (cursor-spec) (let ((case-fold-search nil)) - (while (re-search-forward "%\\([-0-9]+\\)?\\*" nil t) + (while (re-search-forward "%\\([-0-9]+\\)?\\*" nil t) (replace-match "\"(point)\"" t t) (setq cursor-spec t))) ;; Convert TAB commands. (goto-char (point-min)) (while (re-search-forward "%\\([-0-9]+\\)=" nil t) - (replace-match (format "\"(tab %s)\"" (match-string 1)) t t)) + (replace-match (format "\"(tab %s)\"" (match-string 1)) t t)) ;; Convert the buffer into the spec. (goto-char (point-min)) (let ((form (read (current-buffer)))) - (if cursor-spec + (if cursor-spec `(let (gnus-position) ,@(gnus-complex-form-to-spec form spec-alist) (if gnus-position (put-text-property gnus-position (1+ gnus-position) - 'gnus-position t))) + 'gnus-position t))) `(progn ,@(gnus-complex-form-to-spec form spec-alist))))))) @@ -452,8 +448,7 @@ gnus-parse-simple-format spec flist fstring elem result dontinsert user-defined type value pad-width spec-beg cut-width ignore-value tilde-form tilde elem-type extended-spec) - (save-excursion - (gnus-set-work-buffer) + (gnus-with-temp-buffer (insert format) (goto-char (point-min)) (while (re-search-forward "%" nil t) diff --git a/lisp/gnus/gnus-srvr.el b/lisp/gnus/gnus-srvr.el index f66f8427ea..6dfd8d9050 100644 --- a/lisp/gnus/gnus-srvr.el +++ b/lisp/gnus/gnus-srvr.el @@ -24,8 +24,6 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'gnus) (require 'gnus-start) (require 'gnus-spec) @@ -754,8 +752,8 @@ gnus-browse-foreign-server (setq gnus-browse-current-method (gnus-server-to-method server)) (setq gnus-browse-return-buffer return-buffer) (let* ((method gnus-browse-current-method) - (orig-select-method gnus-select-method) - (gnus-select-method method) + (orig-select-methods gnus-select-methods) + (gnus-select-methods (list method)) groups group) (gnus-message 5 "Connecting to %s..." (nth 1 method)) (cond @@ -822,7 +820,7 @@ gnus-browse-foreign-server (lambda (l1 l2) (string< (car l1) (car l2))))) (if gnus-server-browse-in-group-buffer - (let* ((gnus-select-method orig-select-method) + (let* ((gnus-select-methods orig-select-methods) (gnus-group-listed-groups (mapcar (lambda (group) (let ((name @@ -846,7 +844,7 @@ gnus-browse-foreign-server "Gnus: %%b {%s:%s}" (car method) (cadr method)))) (let ((buffer-read-only nil) name - (prefix (let ((gnus-select-method orig-select-method)) + (prefix (let ((gnus-select-methods orig-select-methods)) (gnus-group-prefixed-name "" method)))) (while (setq group (pop groups)) (add-text-properties diff --git a/lisp/gnus/gnus-start.el b/lisp/gnus/gnus-start.el index 44e97d5484..a3360769fb 100644 --- a/lisp/gnus/gnus-start.el +++ b/lisp/gnus/gnus-start.el @@ -29,47 +29,49 @@ (require 'gnus-int) (require 'gnus-spec) (require 'gnus-range) -(require 'gnus-util) (require 'gnus-cloud) (require 'gnus-dbus) +(require 'nnmail) + (autoload 'message-make-date "message") (autoload 'gnus-agent-read-servers-validate "gnus-agent") (autoload 'gnus-agent-save-local "gnus-agent") (autoload 'gnus-agent-possibly-alter-active "gnus-agent") -(declare-function gnus-group-decoded-name "gnus-group" (string)) +(autoload 'gnus-agent-save-active "gnus-agent") +(autoload 'gnus-agentize "gnus-agent" nil t) -(eval-when-compile (require 'cl-lib)) +(declare-function gnus-group-decoded-name "gnus-group" (string)) +(declare-function gnus-group-default-level "gnus-group") (defvar gnus-agent-covered-methods) (defvar gnus-agent-file-loading-local) (defvar gnus-agent-file-loading-cache) (defvar gnus-topic-alist) +(defvar gnus-inhibit-demon) -(defcustom gnus-startup-file (nnheader-concat gnus-home-directory ".newsrc") - "Your `.newsrc' file. -`.newsrc-SERVER' will be used instead if that exists." - :group 'gnus-start - :type 'file) +(defconst gnus-thread-group "gnus-get-unread-articles" + "Identifying prefix for fetching threads.") -(defcustom gnus-backup-startup-file 'never - "Control use of version numbers for backups of `gnus-startup-file'. -This variable takes the same values as the `version-control' -variable." - :version "22.1" +(defcustom gnus-max-seconds-hold-mutex 75 + "Timeout in seconds before relinquishing `gnus-mutex-get-unread-articles'." :group 'gnus-start - :type '(choice (const :tag "Never" never) - (const :tag "If existing" nil) - (other :tag "Always" t))) - -(defcustom gnus-save-startup-file-via-temp-buffer t - "Whether to write the startup file contents to a buffer then save -the buffer or write directly to the file. The buffer is faster -because all of the contents are written at once. The direct write -uses considerably less memory." - :version "22.1" + :type 'integer) + +(defvar gnus-mutex-get-unread-articles (make-mutex gnus-thread-group) + "Updating or displaying state of unread articles are critical sections.") + +(defconst gnus-dot-newsrc (nnheader-concat gnus-home-directory ".newsrc") + "Traditional Usenet .newsrc file. Its time is long past.") + +(defcustom gnus-newsrc-file (nnheader-concat gnus-home-directory ".newsrc.eld") + "So-called El Dingo state file. Do not change this." :group 'gnus-start - :type '(choice (const :tag "Write via buffer" t) - (const :tag "Write directly to file" nil))) + :type 'file) + +(make-obsolete-variable 'gnus-startup-file 'gnus-newsrc-file "28.1") + +(make-obsolete-variable 'gnus-backup-startup-file nil "28.1") +(make-obsolete-variable 'gnus-save-startup-file-via-temp-buffer nil "28.1") (defcustom gnus-init-file (nnheader-concat gnus-home-directory ".gnus") "Your Gnus Emacs Lisp startup file name. @@ -109,7 +111,7 @@ gnus-check-new-newsgroups or killed. When any of the following are true, `gnus-find-new-newsgroups' will instead -ask the servers (primary, secondary, and archive servers) to list new +ask the servers (including the archive server) to list new groups since the last time it checked: 1. This variable is `ask-server'. 2. This variable is a list of select methods (see below). @@ -121,8 +123,7 @@ gnus-check-new-newsgroups longer necessary, so you could safely set `gnus-save-killed-list' to nil. This variable can be a list of select methods which Gnus will query with -the `ask-server' method in addition to the primary, secondary, and archive -servers. +the `ask-server' method in addition to the archive server. E.g.: (setq gnus-check-new-newsgroups @@ -217,24 +218,15 @@ gnus-activate-foreign-newsgroups :type '(choice integer (const :tag "none" nil))) -(defcustom gnus-read-newsrc-file t - "Non-nil means that Gnus will read the `.newsrc' file. -Gnus always reads its own startup file, which is called -\".newsrc.eld\". The file called \".newsrc\" is in a format that can -be readily understood by other newsreaders. If you don't plan on -using other newsreaders, set this variable to nil to save some time on -entry." - :version "21.1" - :group 'gnus-newsrc - :type 'boolean) +(make-obsolete-variable 'gnus-read-newsrc-file nil "28.1") + +(make-obsolete-variable 'gnus-save-newsrc-file 'gnus-save-dot-newsrc "28.1") +(defvaralias 'gnus-save-dot-newsrc 'gnus-save-newsrc-file) (defcustom gnus-save-newsrc-file t - "Non-nil means that Gnus will save the `.newsrc' file. -Gnus always saves its own startup file, which is called -\".newsrc.eld\". The file called \".newsrc\" is in a format that can -be readily understood by other newsreaders. If you don't plan on -using other newsreaders, set this variable to nil to save some time on -exit." + "Save a Usenet .newsrc for nntp groups. +Note the .newsrc is primarily for the benefit of other newsreaders. +Gnus uses .newsrc.eld, not .newsrc." :group 'gnus-newsrc :type 'boolean) @@ -378,6 +370,17 @@ gnus-options-not-subscribe :type '(choice regexp (const :tag "none" nil))) +(defcustom gnus-background-get-unread-articles nil + "Instantiate background thread for `gnus-get-unread-articles' which +covers most of the network retrieval when `gnus-group-get-new-news' is run." + :group 'gnus-start + :type 'boolean + :set (lambda (symbol value) + (set-default symbol value) + (when value (unless (featurep 'threads) + (set-default symbol nil) + (gnus-message 5 "Threads unsupported"))))) + (defcustom gnus-modtime-botch nil "Non-nil means .newsrc should be deleted prior to save. Its use is due to the bogus appearance that .newsrc was modified on @@ -439,27 +442,20 @@ gnus-after-getting-new-news-hook :group 'gnus-group-new :type 'hook) -(defcustom gnus-read-newsrc-el-hook nil - "A hook called after reading the newsrc.eld? file." +(defcustom gnus-read-newsrc-hook nil + "A hook called after reading the newsrc.eld file." :group 'gnus-newsrc :type 'hook) -(defcustom gnus-save-newsrc-hook nil - "A hook called before saving any of the newsrc files." - :group 'gnus-newsrc - :type 'hook) +(make-obsolete-variable 'gnus-read-newsrc-el-hook 'gnus-read-newsrc-hook "28.1") -(defcustom gnus-save-quick-newsrc-hook nil - "A hook called just before saving the quick newsrc file. -Can be used to turn version control on or off." +(defcustom gnus-save-newsrc-hook nil + "A hook called before saving the newsrc.eld file." :group 'gnus-newsrc :type 'hook) -(defcustom gnus-save-standard-newsrc-hook nil - "A hook called just before saving the standard newsrc file. -Can be used to turn version control on or off." - :group 'gnus-newsrc - :type 'hook) +(make-obsolete-variable 'gnus-save-quick-newsrc-hook nil "28.1") +(make-obsolete-variable 'gnus-save-standard-newsrc-hook nil "28.1") (defcustom gnus-group-mode-hook nil "Hook for Gnus group mode." @@ -491,14 +487,17 @@ gnus-newsrc-options-n (defvar gnus-newsrc-last-checked-date nil "Date Gnus last asked server for new newsgroups.") -(defvar gnus-current-startup-file nil - "Startup file for the current host.") - ;; Byte-compiler warning. (defvar gnus-group-line-format) ;; Suggested by Brian Edmonds . (defvar gnus-init-inhibit nil) + +(defvar gnus-thread-start nil + "(lisp-time . thread) when background thread got mutex. +The macro `with-timeout' within thread body is verboten since handlerlist is not +thread-safe in eval.c.") + (defun gnus-read-init-file (&optional inhibit-next) ;; Don't load .gnus if the -q option was used. (when init-file-user @@ -594,7 +593,7 @@ gnus-subscribe-alphabetically (defun gnus-subscribe-hierarchically (newgroup) "Subscribe new NEWGROUP and insert it in hierarchical newsgroup order." ;; Basic ideas by mike-w@cs.aukuni.ac.nz (Mike Williams) - (with-current-buffer (nnheader-find-file-noselect gnus-current-startup-file) + (with-current-buffer (nnheader-find-file-noselect gnus-dot-newsrc) (prog1 (let ((groupkey newgroup) before) (while (and (not before) groupkey) @@ -710,9 +709,8 @@ gnus-clear-system gnus-ephemeral-servers nil) (gnus-shutdown 'gnus) ;; Kill the startup file. - (and gnus-current-startup-file - (get-file-buffer gnus-current-startup-file) - (kill-buffer (get-file-buffer gnus-current-startup-file))) + (when-let ((buffer (get-file-buffer gnus-newsrc-file))) + (kill-buffer buffer)) ;; Clear the dribble buffer. (gnus-dribble-clear) ;; Kill global KILL file buffer. @@ -755,7 +753,6 @@ gnus-1 (and (numberp arg) (> arg 0) (max (car gnus-group-list-mode) arg)))) - (gnus-clear-system) (gnus-splash) (gnus-run-hooks 'gnus-before-startup-hook) @@ -769,8 +766,8 @@ gnus-1 (add-to-list 'gnus-predefined-server-alist (cons "native" gnus-select-method))) - (if gnus-agent - (gnus-agentize)) + (when gnus-agent + (gnus-agentize)) (let ((level (and (numberp arg) (> arg 0) arg)) did-connect) @@ -784,9 +781,6 @@ gnus-1 ;; Couldn't connect to the server, so bail out. (gnus-group-quit) (gnus-run-hooks 'gnus-startup-hook) - ;; Find the current startup file name. - (setq gnus-current-startup-file - (gnus-make-newsrc-file gnus-startup-file)) ;; Read the dribble file. (when (or gnus-child gnus-use-dribble-file) @@ -801,7 +795,7 @@ gnus-1 (gnus-dbus-register-sleep-signal)) (gnus-start-draft-setup) ;; Generate the group buffer. - (gnus-group-list-groups level) + (gnus-group-list-groups level) (gnus-group-first-unread-group) (gnus-configure-windows 'group) (gnus-group-set-mode-line) @@ -820,7 +814,6 @@ gnus-start-draft-setup (gnus-group-set-parameter "nndraft:drafts" 'gnus-dummy '((gnus-draft-mode))))) - ;;; ;;; Dribble file ;;; @@ -833,8 +826,8 @@ gnus-dribble-file-name (concat (if gnus-dribble-directory (concat (file-name-as-directory gnus-dribble-directory) - (file-name-nondirectory gnus-current-startup-file)) - gnus-current-startup-file) + (file-name-nondirectory gnus-newsrc-file)) + gnus-newsrc-file) "-dribble")) (defun gnus-dribble-enter (string &optional regexp) @@ -882,32 +875,25 @@ gnus-dribble-read-file (buffer-disable-undo) (bury-buffer (current-buffer)) (set-buffer-modified-p nil) - (let ((auto (make-auto-save-file-name)) - (gnus-dribble-ignore t) - (purpose nil) - modes) - (when (or (file-exists-p auto) (file-exists-p dribble-file)) - ;; Load whichever file is newest -- the auto save file - ;; or the "real" file. - (if (file-newer-than-file-p auto dribble-file) - (nnheader-insert-file-contents auto) - (nnheader-insert-file-contents dribble-file)) + (let* ((gnus-dribble-ignore t) + (auto (make-auto-save-file-name)) + (state (if (file-newer-than-file-p auto dribble-file) + auto + dribble-file)) + modes) + (when (file-exists-p state) + (nnheader-insert-file-contents state) (unless (zerop (buffer-size)) (set-buffer-modified-p t)) ;; Set the file modes to reflect the .newsrc file modes. (save-buffer) - (when (and (setq modes (file-modes gnus-current-startup-file)) + (when (and (setq modes (file-modes gnus-newsrc-file)) (file-exists-p dribble-file)) (gnus-set-file-modes dribble-file modes)) (goto-char (point-min)) - (when (search-forward "Gnus was exited on purpose" nil t) - (setq purpose t)) ;; Possibly eval the file later. (when (or gnus-always-read-dribble-file - (gnus-y-or-n-p - (if purpose - "Gnus exited on purpose without saving; read auto-save file anyway? " - "Gnus auto-save file exists. Do you want to read it? "))) + (gnus-y-or-n-p (format "Read unsaved state %s? " state))) (setq gnus-dribble-eval-file t))))))) (defun gnus-dribble-eval-file () @@ -941,27 +927,23 @@ gnus-dribble-clear (set-buffer-modified-p nil) (setq buffer-saved-size (buffer-size))))) - ;;; ;;; Active & Newsrc File Handling ;;; -(defun gnus-setup-news (&optional rawfile level dont-connect) +(defun gnus-setup-news (&optional _force level dont-connect) "Setup news information. -If RAWFILE is non-nil, the .newsrc file will also be read. If LEVEL is non-nil, the news will be set up at level LEVEL." - (require 'nnmail) - (let ((init (not (and gnus-newsrc-alist gnus-active-hashtb (not rawfile)))) + (let ((init (or (not gnus-newsrc-alist) (not gnus-active-hashtb))) ;; Binding this variable will inhibit multiple fetchings ;; of the same mail source. (nnmail-fetched-sources (list t))) - (when init ;; Clear some variables to re-initialize news information. (setq gnus-newsrc-alist nil gnus-active-hashtb nil) ;; Read the newsrc file and create `gnus-newsrc-hashtb'. - (gnus-read-newsrc-file rawfile)) + (gnus-read-newsrc-file)) ;; Make sure the archive server is available to all and sundry. (let ((method (or (and (stringp gnus-message-archive-method) @@ -1163,21 +1145,18 @@ gnus-matches-options-n (defun gnus-ask-server-for-new-groups () (let* ((new-date (message-make-date)) (date (or gnus-newsrc-last-checked-date new-date)) - (methods (cons gnus-select-method - (nconc - (when (gnus-archive-server-wanted-p) - (list "archive")) - (append - (and (consp gnus-check-new-newsgroups) - gnus-check-new-newsgroups) - gnus-secondary-select-methods)))) + (methods (nconc + (when (gnus-archive-server-wanted-p) + (list "archive")) + (append + (and (consp gnus-check-new-newsgroups) + gnus-check-new-newsgroups) + gnus-select-methods))) (groups 0) new-newsgroups got-new method hashtb ;; group gnus-override-subscribe-method) (unless gnus-killed-hashtb (gnus-make-hashtable-from-killed)) - ;; Go through both primary and secondary select methods and - ;; request new newsgroups. (while (setq method (gnus-server-get-method nil (pop methods))) (setq new-newsgroups nil gnus-override-subscribe-method method) @@ -1424,81 +1403,56 @@ gnus-cache-active-hashtb (when (> (cdr cache-active) (cdr active)) (setcdr active (cdr cache-active)))))))) -(defun gnus-activate-group (group &optional scan dont-check method - dont-sub-check) +(defun gnus-activate-group (group + &optional scan dont-check method dont-sub-check) "Check whether a group has been activated or not. If SCAN, request a scan of that group as well. If METHOD, use that select method instead of determining the method based on the group name. If DONT-CHECK, don't check whether the group actually exists. If DONT-SUB-CHECK or DONT-CHECK, don't let the backend check whether the group actually exists." - (let ((method (or method (inline (gnus-find-method-for-group group)))) - active) - (and (inline (gnus-check-server method)) - ;; We escape all bugs and quit here to make it possible to - ;; continue if a group is so out-there that it reports bugs - ;; and stuff. - (progn - (and scan - (gnus-check-backend-function 'request-scan (car method)) - (gnus-request-scan group method)) - t) - (if (or debug-on-error debug-on-quit) - (inline (gnus-request-group group (or dont-sub-check dont-check) - method - (gnus-get-info group))) - (condition-case nil - (inline (gnus-request-group group (or dont-sub-check dont-check) - method - (gnus-get-info group))) - (quit - (if debug-on-quit - (debug "Quit") - (message "Quit activating %s" group)) - nil))) - (unless dont-check - (setq active (gnus-parse-active)) - ;; If there are no articles in the group, the GROUP - ;; command may have responded with the `(0 . 0)'. We - ;; ignore this if we already have an active entry - ;; for the group. - (if (and (zerop (or (car active) 0)) - (zerop (or (cdr active) 0)) - (gnus-active group)) - (gnus-active group) - - ;; If a cache is present, we may have to alter the active info. - (when gnus-use-cache - (inline (gnus-cache-possibly-alter-active - group active))) - - ;; If the agent is enabled, we may have to alter the active info. - (when gnus-agent - (gnus-agent-possibly-alter-active group active)) - - (gnus-set-active group active) - ;; Return the new active info. - active))))) + (setq method (or method (gnus-find-method-for-group group))) + (when (gnus-check-server method) + (when scan + (gnus-check-backend-function 'request-scan (car method)) + (gnus-request-scan group method)) + (when (and (gnus-request-group group + (or dont-sub-check dont-check) + method + (gnus-get-info group)) + (not dont-check)) + (let ((new-active (gnus-parse-active)) + (old-active (gnus-active group))) + ;; If new active is `(0 . 0)`, then return existing active. + (if (and old-active + (cl-every (lambda (e) (or (not (integerp e)) + (zerop e))) + `(,(car new-active) ,(cdr new-active)))) + old-active + (when gnus-use-cache + (gnus-cache-possibly-alter-active group new-active)) + (when gnus-agent + (gnus-agent-possibly-alter-active group new-active)) + (gnus-set-active group new-active) + new-active))))) (defun gnus-get-unread-articles-in-group (info active &optional update) (when (and info active) ;; Allow the backend to update the info in the group. (when (and update (gnus-request-update-info - info (inline (gnus-find-method-for-group - (gnus-info-group info))))) + info (gnus-find-method-for-group (gnus-info-group info)))) (gnus-activate-group (gnus-info-group info) nil t)) - (let* ((range (gnus-info-read info)) - (num 0)) + (let ((range (gnus-info-read info)) + (num 0)) ;; These checks are present in gnus-activate-group but skipped ;; due to setting dont-check in the preceding call. ;; If a cache is present, we may have to alter the active info. (when (and gnus-use-cache info) - (inline (gnus-cache-possibly-alter-active - (gnus-info-group info) active))) + (gnus-cache-possibly-alter-active (gnus-info-group info) active)) ;; If the agent is enabled, we may have to alter the active info. (when (and gnus-agent info) @@ -1575,197 +1529,300 @@ gnus-get-unread-articles-in-group (setcar (gnus-group-entry (gnus-info-group info)) num)) num))) -;; Go though `gnus-newsrc-alist' and compare with `gnus-active-hashtb' -;; and compute how many unread articles there are in each group. -(defun gnus-get-unread-articles (&optional level dont-connect one-level) +(defmacro gnus-scope-globals (&rest forms) + "Sandbox globals for thread safety." + (declare (indent 0)) + (let ((variables (quote (gnus-newsgroup-name + gnus-newsgroup-marked + gnus-newsgroup-spam-marked + gnus-newsgroup-unreads + gnus-current-headers + gnus-newsgroup-data + gnus-summary-buffer + gnus-article-buffer + gnus-original-article-buffer + gnus-article-current + gnus-current-article + gnus-reffed-article-number + gnus-current-score-file + gnus-newsgroup-charset)))) + `(progn + ,(cons 'inline (mapcar (lambda (v) (list 'defvar v)) variables)) + (let ,(mapcar (apply-partially #'make-list 2) variables) + ,@forms)))) + +(defun gnus-thread-body (thread-name mtx fns) + "Errors need to be trapped for a clean exit. +Else we get unblocked but permanently yielded threads." + (let ((working (get-buffer-create (format " *%s*" thread-name))) + (inhibit-debugger t) + debug-on-quit + debug-on-error) + ;; once context switch occurs handlerlist in eval.c(throw) is lost + (unwind-protect + (condition-case err + (with-mutex mtx + (setq gnus-thread-start (cons (current-time) (current-thread))) + (with-current-buffer working + (gnus-message-with-timestamp "gnus-thread-body: start %s <%s>" + thread-name (buffer-name)) + ;; buffer-locals not thread-safe (avoid them) + (let (gnus-run-thread--subresult + current-fn + (gnus-inhibit-demon t) + (nntp-server-buffer (current-buffer))) + (condition-case err + (dolist (fn fns) + (setq current-fn fn) + (setq gnus-run-thread--subresult + (funcall fn gnus-run-thread--subresult)) + (thread-yield)) + (error + (ignore-errors (mutex-unlock mtx)) + ;; feed current-fn to outer condition-case + (error "dolist: '%s' in %s" + (error-message-string err) current-fn)))))) + (error (gnus-message-with-timestamp + "gnus-thread-body: error %s '%s'" + thread-name (error-message-string err)))) + (let (kill-buffer-query-functions) + (kill-buffer working)) + (setq gnus-thread-start nil) + (ignore-errors (mutex-unlock mtx)) + (gnus-message-with-timestamp "gnus-thread-body: finish %s" thread-name)))) + +(defun gnus-thread-group-running-p (thread-group) + (when-let ((thr (cl-some (lambda (thr) + (when (cl-search thread-group (thread-name thr)) + thr)) + (all-threads)))) + (if (thread-live-p thr) + thr + (prog1 nil + (thread-signal thr 'error nil))))) + +(defun gnus-run-thread (label mtx thread-group &rest fns) + "MTX, if non-nil, is the mutex for the new thread. +THREAD-GROUP is string useful for naming working buffer and threads. +All FNS must finish before MTX is released." + (when fns + (let ((thread-name (concat thread-group "-" label))) + (make-thread + (apply-partially #'gnus-thread-body thread-name mtx fns) + thread-name)))) + +(defun gnus-chain-arg (tack-p f &rest args) + (lambda (prev) + (apply f (append args (when tack-p (list prev)))))) + +(defun gnus-time-out-thread () + (interactive) + (when gnus-thread-start + (cl-destructuring-bind (started . thread) + gnus-thread-start + (when (time-less-p + (time-add started gnus-max-seconds-hold-mutex) + nil) + (setq gnus-thread-start nil) + (if (thread-live-p thread) + (progn + (gnus-message-with-timestamp + "gnus-time-out-thread: signal quit %s" (thread-name thread)) + (thread-signal thread 'quit nil)) + (gnus-message-with-timestamp + "gnus-time-out-thread: race on dead %s" (thread-name thread))))))) + +(cl-defun gnus-get-unread-articles (&optional + requested-level + dont-connect + one-level + &aux + (level (gnus-group-default-level requested-level t))) + "Workhorse of `gnus-group-get-new-news'." (setq gnus-server-method-cache nil) - (require 'gnus-agent) (defvar gnus-agent-article-local-times) - (let* ((newsrc (cdr gnus-newsrc-alist)) - (alevel (or level gnus-activate-level (1+ gnus-level-subscribed))) - (foreign-level - (or - level - (min - (cond ((and gnus-activate-foreign-newsgroups - (not (numberp gnus-activate-foreign-newsgroups))) - (1+ gnus-level-subscribed)) - ((numberp gnus-activate-foreign-newsgroups) - gnus-activate-foreign-newsgroups) - (t 0)) - alevel))) - (methods-cache nil) - (type-cache nil) - (gnus-agent-article-local-times 0) - (archive-method (gnus-server-to-method "archive")) - info group active method cmethod - method-type method-group-list entry) - (gnus-message 6 "Checking new news...") - - (while newsrc - (setq active (gnus-active (setq group (gnus-info-group - (setq info (pop newsrc)))))) - ;; First go through all the groups, see what select methods they - ;; belong to, and then collect them into lists per unique select - ;; method. - (if (not (setq method (gnus-info-method info))) - (setq method gnus-select-method) - ;; There may be several similar methods. Possibly extend the - ;; method. - (if (setq cmethod (assoc method methods-cache)) - (setq method (cdr cmethod)) - (setq cmethod (if (stringp method) - (gnus-server-to-method method) - (inline (gnus-find-method-for-group - (gnus-info-group info) info)))) - (push (cons method cmethod) methods-cache) - (setq method cmethod))) - (setq method-group-list (assoc method type-cache)) - (unless method-group-list - (setq method-type - (cond - ((or (gnus-secondary-method-p method) - (and (gnus-archive-server-wanted-p) - (gnus-methods-equal-p archive-method method))) - 'secondary) - ((inline (gnus-server-equal gnus-select-method method)) - 'primary) - (t - 'foreign))) - (push (setq method-group-list (list method method-type nil nil)) - type-cache)) - ;; Only add groups that need updating. - (if (or (and foreign-level (null (numberp foreign-level))) - (funcall (if one-level #'= #'<=) (gnus-info-level info) - (if (eq (cadr method-group-list) 'foreign) - foreign-level - alevel))) - (setcar (nthcdr 2 method-group-list) - (cons info (nth 2 method-group-list))) - ;; The group is inactive, so we nix out the number of unread articles. - ;; It leads `(gnus-group-unread group)' to return t. See also - ;; `gnus-group-prepare-flat'. - (unless active - (when (setq entry (gnus-group-entry group)) - (setcar entry t))))) - - ;; Sort the methods based so that the primary and secondary - ;; methods come first. This is done for legacy reasons to try to - ;; ensure that side-effect behavior doesn't change from previous - ;; Gnus versions. - (setq type-cache - (sort (nreverse type-cache) - (lambda (c1 c2) - (< (gnus-method-rank (cadr c1) (car c1)) - (gnus-method-rank (cadr c2) (car c2)))))) - ;; Go through the list of servers and possibly extend methods that - ;; aren't equal (and that need extension; i.e., they are async). - (let ((methods nil)) - (dolist (elem type-cache) - (cl-destructuring-bind (method _method-type infos _dummy) elem - (let ((gnus-opened-servers methods)) - (when (and (gnus-similar-server-opened method) - (gnus-check-backend-function - 'retrieve-group-data-early (car method))) - (setq method (gnus-server-extend-method - (gnus-info-group (car infos)) - method)) - (setcar elem method)) - (push (list method 'ok) methods))))) - - ;; If we have primary/secondary select methods, but no groups from - ;; them, we still want to issue a retrieval request from them. - (unless dont-connect - (dolist (method (cons gnus-select-method - gnus-secondary-select-methods)) - (when (and (not (assoc method type-cache)) - (gnus-check-backend-function 'request-list (car method))) - (with-current-buffer nntp-server-buffer - (gnus-read-active-file-1 method nil))))) - - ;; Clear out all the early methods. - (dolist (elem type-cache) - (cl-destructuring-bind (method _method-type infos _dummy) elem - (when (and method - infos - (gnus-check-backend-function - 'retrieve-group-data-early (car method)) - (not (gnus-method-denied-p method))) - (when (ignore-errors (gnus-get-function method 'open-server)) - (unless (gnus-server-opened method) - (gnus-open-server method)) - (when (gnus-server-opened method) - ;; Just mark this server as "cleared". - (gnus-retrieve-group-data-early method nil)))))) - - ;; Start early async retrieval of data. - (let ((done-methods nil) - sanity-spec) - (dolist (elem type-cache) - (cl-destructuring-bind (method _method-type infos _dummy) elem - (setq sanity-spec (list (car method) (cadr method))) - (when (and method infos - (not (gnus-method-denied-p method))) - ;; If the open-server method doesn't exist, then the method - ;; itself doesn't exist, so we ignore it. - (if (not (ignore-errors (gnus-get-function method 'open-server))) - (setq type-cache (delq elem type-cache)) - (unless (gnus-server-opened method) - (gnus-open-server method)) - (when (and - ;; This is a sanity check, so that we never - ;; attempt to start two async requests to the - ;; same server, because that will fail. This - ;; should never happen, since the methods should - ;; be unique at this point, but apparently it - ;; does happen in the wild with some setups. - (not (member sanity-spec done-methods)) - (gnus-server-opened method) - (gnus-check-backend-function - 'retrieve-group-data-early (car method))) - (push sanity-spec done-methods) - (when (gnus-check-backend-function 'request-scan (car method)) - (gnus-request-scan nil method)) - ;; Store the token we get back from -early so that we - ;; can pass it to -finish later. - (setcar (nthcdr 3 elem) - (gnus-retrieve-group-data-early method infos)))))))) - - ;; Do the rest of the retrieval. - (dolist (elem type-cache) - (cl-destructuring-bind (method _method-type infos early-data) elem - (when (and method infos - (not (gnus-method-denied-p method))) - (let ((updatep (gnus-check-backend-function - 'request-update-info (car method)))) - ;; See if any of the groups from this method require updating. - (gnus-read-active-for-groups method infos early-data) - (dolist (info infos) - (inline (gnus-get-unread-articles-in-group - info (gnus-active (gnus-info-group info)) - updatep))))))) - (gnus-message 6 "Checking new news...done"))) - -(defun gnus-method-rank (type method) - (cond - ;; Get info for virtual groups last. - ((eq (car method) 'nnvirtual) - 200) - ((eq type 'primary) - 1) - ;; Compute the rank of the secondary methods based on where they - ;; are in the secondary select list. - ((eq type 'secondary) - (let ((i 2)) - (cl-block nil - (cl-dolist (smethod gnus-secondary-select-methods) - (when (equal method smethod) - (cl-return i)) - (cl-incf i)) - i))) - ;; Just say that all foreign groups have the same rank. - (t - 100))) + (cl-assert (eq (current-thread) main-thread)) + (when gnus-background-get-unread-articles + (unless (cl-find-if (lambda (timer) + (eq (timer--function timer) + #'gnus-time-out-thread)) + timer-list) + (run-at-time + nil + (/ gnus-max-seconds-hold-mutex 2) + #'gnus-time-out-thread))) + (if-let ((pending (gnus-thread-group-running-p gnus-thread-group))) + (gnus-message 3 "gnus-get-unread-articles: %s still running" pending) + (let* ((newsrc (cdr gnus-newsrc-alist)) + (alevel (or level gnus-activate-level (1+ gnus-level-subscribed))) + (foreign-level + (or + level + (min + (cond ((and gnus-activate-foreign-newsgroups + (not (numberp gnus-activate-foreign-newsgroups))) + (1+ gnus-level-subscribed)) + ((numberp gnus-activate-foreign-newsgroups) + gnus-activate-foreign-newsgroups) + (t 0)) + alevel))) + (gnus-agent-article-local-times 0) + (archive-method (gnus-server-to-method "archive")) + infos-by-method) + (gnus-message 6 "Checking new news...") + (while newsrc + (when-let ((info (pop newsrc)) + (group (gnus-info-group info)) + (method (gnus-find-method-for-group group info)) + (backend (car method))) + (if (or (and foreign-level (not (numberp foreign-level))) + (funcall (if one-level #'= #'<=) + (gnus-info-level info) + (if (or (gnus-server-equal gnus-select-method method) + (gnus-secondary-method-p method) + (and (gnus-archive-server-wanted-p) + (gnus-methods-equal-p archive-method method))) + alevel + foreign-level))) + (push info (alist-get method infos-by-method nil nil #'equal)) + ;; The group is inactive, so we nix out the number of unread articles. + ;; It leads `(gnus-group-unread group)' to return t. See also + ;; `gnus-group-prepare-flat'. + (unless (gnus-active group) + (when-let ((entry (gnus-group-entry group))) + (setcar entry t)))))) + + ;; "Extend" means changing "nnimap" to "nnimap+subaccount" + (let (methods) + (dolist (elem infos-by-method) + (cl-destructuring-bind (method &rest infos) elem + (let ((gnus-opened-servers methods)) + (when (and (gnus-similar-server-opened method) + (gnus-check-backend-function + 'retrieve-group-data-early (car method))) + (setq method (gnus-server-extend-method + (gnus-info-group (car infos)) + method)) + (setcar elem method)) + (push (list method 'ok) methods))))) + + ;; For methods with no groups to update, we still request-list if supported. + (unless dont-connect + (dolist (method gnus-select-methods) + (when (and (not (assoc method infos-by-method)) + (gnus-check-backend-function 'request-list (car method))) + (with-current-buffer nntp-server-buffer + (gnus-read-active-file-1 method nil))))) + + ;; Must be able to `gnus-open-server' + (setq infos-by-method + (cl-remove-if-not + (lambda (elem) + (cl-destructuring-bind (method &rest infos) elem + (and (ignore-errors (gnus-get-function method 'open-server)) + (memq (car method) (mapcar #'car gnus-select-methods))))) + infos-by-method)) + + (let (methods + (coda (apply-partially + (lambda (level*) + (gnus-message-with-timestamp "gnus-get-unread-articles: all done") + (when-let ((timer (cl-find-if (lambda (timer) + (eq (timer--function timer) + #'gnus-time-out-thread)) + timer-list))) + (cancel-timer timer)) + (gnus-group-list-groups level*) + (gnus-run-hooks 'gnus-after-getting-new-news-hook) + (gnus-group-list-groups) + (redisplay t)) + (and (numberp level) + (max (or (and (numberp (car gnus-group-list-mode)) + (car gnus-group-list-mode)) + (gnus-group-default-level)) + level))))) + (mapc (lambda (elem) + (cl-destructuring-bind + (method &rest infos + &aux + (backend (car method)) + (already-p + (cl-some (apply-partially + #'gnus-methods-equal-p method) + methods)) + (denied-p (gnus-method-denied-p method)) + (scan-p (gnus-check-backend-function 'request-scan backend)) + (early-p (gnus-check-backend-function + 'retrieve-group-data-early backend)) + (update-p (gnus-check-backend-function + 'request-update-info backend)) + commands) + elem + (when (and method infos (not denied-p) (not already-p)) + (push method methods) + (gnus-push-end (gnus-chain-arg + nil + #'gnus-open-server + method) + commands) + (when early-p + (when scan-p + (gnus-push-end (gnus-chain-arg nil #'gnus-request-scan nil method) + commands)) + ;; Store the token we get back from -early so that we + ;; can pass it to -finish later. + (gnus-push-end (gnus-chain-arg + nil + #'gnus-retrieve-group-data-early + method infos) + commands)) + (gnus-push-end (gnus-chain-arg + t + #'gnus-read-active-for-groups + method infos) + commands) + (gnus-push-end (gnus-chain-arg + nil + (lambda (infos* update-p*) + (mapc (lambda (info) + (gnus-get-unread-articles-in-group + info + (gnus-active (gnus-info-group info)) + update-p*) + (gnus-group-update-group (gnus-info-group info) t)) + infos*) + (gnus-message 6 "Checking new news...done")) + infos update-p) + commands) + (if gnus-background-get-unread-articles + (progn + (add-function + :before-while (var coda) + (apply-partially + (lambda (thread-group* &rest _args) + "Proceed with CODA if I'm the last one." + (<= (cl-count thread-group* + (all-threads) + :test (lambda (s thr) + (cl-search s (thread-name thr)))) + 1)) + gnus-thread-group)) + (gnus-push-end (gnus-chain-arg nil coda) commands) + (apply #'gnus-run-thread + (mapconcat (apply-partially #'format "%s") + (cl-subseq method 0 (min (length method) 2)) + "-") + gnus-mutex-get-unread-articles + gnus-thread-group + commands)) + (let (gnus-run-thread--subresult) + (mapc (lambda (fn) + (setq gnus-run-thread--subresult + (funcall fn gnus-run-thread--subresult))) + commands)))))) + infos-by-method) + (unless gnus-background-get-unread-articles + (funcall coda)))))) (defun gnus-read-active-for-groups (method infos early-data) (with-current-buffer nntp-server-buffer @@ -1776,7 +1833,7 @@ gnus-read-active-for-groups early-data (gnus-check-backend-function 'finish-retrieve-group-infos (car method)) (or (not (gnus-agent-method-p method)) - (gnus-online method))) + (gnus-online method))) (gnus-finish-retrieve-group-infos method infos early-data) ;; We may have altered the data now, so mark the dribble buffer ;; as dirty so that it gets saved. @@ -1785,12 +1842,12 @@ gnus-read-active-for-groups ;; Most backends have -retrieve-groups. ((gnus-check-backend-function 'retrieve-groups (car method)) (when (gnus-check-backend-function 'request-scan (car method)) - (gnus-request-scan nil method)) + (gnus-request-scan nil method)) (let (groups) - (gnus-read-active-file-2 - (dolist (info infos (nreverse groups)) - (push (gnus-group-real-name (gnus-info-group info)) groups)) - method))) + (gnus-read-active-file-2 + (dolist (info infos (nreverse groups)) + (push (gnus-group-real-name (gnus-info-group info)) groups)) + method))) ;; Virtually all backends have -request-list. ((gnus-check-backend-function 'request-list (car method)) (gnus-read-active-file-1 method nil)) @@ -1798,7 +1855,7 @@ gnus-read-active-for-groups ;; by one. (t (dolist (info infos) - (gnus-activate-group (gnus-info-group info) t nil method t)))))) + (gnus-activate-group (gnus-info-group info) t nil method t)))))) (defun gnus-make-hashtable-from-newsrc-alist () "Create a hash table from `gnus-newsrc-alist'. @@ -1998,10 +2055,12 @@ gnus-read-active-file (if (and (not not-native) (gnus-check-server gnus-select-method)) ;; The native server is available. - (cons gnus-select-method gnus-secondary-select-methods) + gnus-select-methods ;; The native server is down, so we just do the ;; secondary ones. - gnus-secondary-select-methods) + (cl-remove-if + (lambda (method) (gnus-method-equal method gnus-select-method)) + gnus-select-methods)) ;; Also read from the archive server. (when (gnus-archive-server-wanted-p) (list "archive"))))) @@ -2036,9 +2095,7 @@ gnus-read-active-file-1 (gnus-message 5 "%s" mesg) (when (gnus-check-server method) ;; Request that the backend scan its incoming messages. - (when (and (or (and gnus-agent - (gnus-online method)) - (not gnus-agent)) + (when (and (or (not gnus-agent) (gnus-online method)) (gnus-check-backend-function 'request-scan (car method))) (gnus-request-scan nil method)) (cond @@ -2215,50 +2272,35 @@ gnus-groups-to-gnus-format (error (remhash group hashtb))) (forward-line 1)))))) -(defun gnus-read-newsrc-file (&optional force) - "Read startup file. -If FORCE is non-nil, the .newsrc file is read." - ;; Reset variables that might be defined in the .newsrc.eld file. - (let ((variables (remove 'gnus-format-specs gnus-variable-list))) - (while variables - (set (car variables) nil) - (setq variables (cdr variables)))) - (let* ((newsrc-file gnus-current-startup-file) - (quick-file (concat newsrc-file ".el"))) - (save-excursion - ;; We always load the .newsrc.eld file. If always contains - ;; much information that can not be gotten from the .newsrc - ;; file (ticked articles, killed groups, foreign methods, etc.) - (gnus-read-newsrc-el-file quick-file) - - (when (and gnus-read-newsrc-file - (file-exists-p gnus-current-startup-file) - (or force - (and (file-newer-than-file-p newsrc-file quick-file) - (file-newer-than-file-p newsrc-file - (concat quick-file "d"))) - (not gnus-newsrc-alist))) - ;; We read the .newsrc file. Note that if there if a - ;; .newsrc.eld file exists, it has already been read, and - ;; the `gnus-newsrc-hashtb' has been created. While reading - ;; the .newsrc file, Gnus will only use the information it - ;; can find there for changing the data already read - - ;; i. e., reading the .newsrc file will not trash the data - ;; already read (except for read articles). - (gnus-message 5 "Reading %s..." newsrc-file) - (with-current-buffer (nnheader-find-file-noselect newsrc-file) - (buffer-disable-undo) - (gnus-newsrc-to-gnus-format) - (kill-buffer (current-buffer)) - (gnus-message 5 "Reading %s...done" newsrc-file))) - - ;; Convert old to new. - (gnus-convert-old-newsrc) - (gnus-clean-old-newsrc)))) - -(defun gnus-clean-old-newsrc (&optional _force) - ;; Currently no cleanups. - ) +(defun gnus-read-newsrc-file (&optional _force) + (dolist (var (remove 'gnus-format-specs gnus-variable-list)) + (set var nil)) + (when (file-exists-p gnus-newsrc-file) + (gnus-message 5 "Reading %s..." gnus-newsrc-file) + (gnus-load gnus-newsrc-file)) + (when (and (file-exists-p gnus-dot-newsrc) (not gnus-newsrc-alist)) + (gnus-message 5 "Reading %s..." gnus-dot-newsrc) + (let ((buffer (nnheader-find-file-noselect gnus-dot-newsrc))) + (unwind-protect + (with-current-buffer buffer + (gnus-newsrc-to-gnus-format) + (gnus-message 5 "Reading %s...done" gnus-dot-newsrc)) + (kill-buffer buffer)))) + (gnus-make-hashtable-from-newsrc-alist) + (setq gnus-topic-alist + (mapcar + (lambda (elt) + (cl-destructuring-bind (topic . groups) + elt + (cons topic + (mapcar (lambda (group) + (if (string-match-p "[^[:ascii:]]" group) + (gnus-group-decoded-name group) + group)) + groups)))) + gnus-topic-alist)) + (gnus-run-hooks 'gnus-read-newsrc-hook) + (gnus-convert-old-newsrc)) (defun gnus-convert-old-newsrc () "Convert old newsrc formats into the current format, if needed." @@ -2383,43 +2425,6 @@ gnus-killed-assoc (defvar gnus-marked-assoc) (defvar gnus-newsrc-assoc) -(defun gnus-read-newsrc-el-file (file) - (let ((ding-file (concat file "d"))) - (when (file-exists-p ding-file) - ;; We always, always read the .eld file. - (gnus-message 5 "Reading %s..." ding-file) - (let (gnus-newsrc-assoc) - (gnus-load ding-file) - ;; Older versions of `gnus-format-specs' are no longer valid - ;; in Oort Gnus 0.01. - (let ((version - (and gnus-newsrc-file-version - (gnus-continuum-version gnus-newsrc-file-version)))) - (when (or (not version) - (< version 5.090009)) - (setq gnus-format-specs gnus-default-format-specs))) - (when gnus-newsrc-assoc - (setq gnus-newsrc-alist gnus-newsrc-assoc)))) - (gnus-make-hashtable-from-newsrc-alist) - (when gnus-topic-alist - (setq gnus-topic-alist - (mapcar - (lambda (elt) - (cons (car elt) - (mapcar (lambda (g) - (if (string-match-p "[^[:ascii:]]" g) - (gnus-group-decoded-name g) - g)) - (cdr elt)))) - gnus-topic-alist))) - (when (file-newer-than-file-p file ding-file) - ;; Old format quick file - (gnus-message 5 "Reading %s..." file) - ;; The .el file is newer than the .eld file, so we read that one - ;; as well. - (gnus-read-old-newsrc-el-file file))) - (gnus-run-hooks 'gnus-read-newsrc-el-hook)) - ;; Parse the old-style quick startup file (defun gnus-read-old-newsrc-el-file (file) (let (newsrc killed marked group m info) @@ -2475,19 +2480,9 @@ gnus-read-old-newsrc-el-file (setq gnus-newsrc-alist (nreverse gnus-newsrc-alist)) (gnus-make-hashtable-from-newsrc-alist))) -(defun gnus-make-newsrc-file (file) - "Make server dependent file name by catenating FILE and server host name." - (let* ((file (expand-file-name file nil)) - (real-file (concat file "-" (nth 1 gnus-select-method)))) - (if (or (file-exists-p real-file) - (file-exists-p (concat real-file ".el")) - (file-exists-p (concat real-file ".eld"))) - real-file - file))) - (defun gnus-newsrc-to-gnus-format () - (setq gnus-newsrc-options "") - (setq gnus-newsrc-options-n nil) + (setq gnus-newsrc-options "" + gnus-newsrc-options-n nil) (unless gnus-active-hashtb (setq gnus-active-hashtb (gnus-make-hashtable 4000))) @@ -2675,58 +2670,57 @@ gnus-newsrc-to-gnus-format ;; groups will be ignored. Note that "options -n !all rec.all" is very ;; different from "options -n rec.all !all". (defun gnus-newsrc-parse-options (options) - (let (out eol) - (save-excursion - (gnus-set-work-buffer) - (insert (regexp-quote options)) - ;; First we treat all continuation lines. - (goto-char (point-min)) - (while (re-search-forward "\n[ \t]+" nil t) - (replace-match " " t t)) - ;; Then we transform all "all"s into ".+"s. - (goto-char (point-min)) - (while (re-search-forward "\\ball\\b" nil t) - (replace-match ".+" t t)) - (goto-char (point-min)) - ;; We remove all other options than the "-n" ones. - (while (re-search-forward "[ \t]-[^n][^-]*" nil t) - (replace-match " ") - (forward-char -1)) - (goto-char (point-min)) + (gnus-with-temp-buffer + (insert (regexp-quote options)) + ;; First we treat all continuation lines. + (goto-char (point-min)) + (while (re-search-forward "\n[ \t]+" nil t) + (replace-match " " t t)) + ;; Then we transform all "all"s into ".+"s. + (goto-char (point-min)) + (while (re-search-forward "\\ball\\b" nil t) + (replace-match ".+" t t)) + (goto-char (point-min)) + ;; We remove all other options than the "-n" ones. + (while (re-search-forward "[ \t]-[^n][^-]*" nil t) + (replace-match " ") + (forward-char -1)) + (goto-char (point-min)) - ;; We are only interested in "options -n" lines - we - ;; ignore the other option lines. - (while (re-search-forward "[ \t]-n" nil t) - (setq eol - (or (save-excursion - (and (re-search-forward "[ \t]-n" (point-at-eol) t) - (- (point) 2))) - (point-at-eol))) - ;; Search for all "words"... - (while (re-search-forward "[^ \t,\n]+" eol t) - (if (eq (char-after (match-beginning 0)) ?!) - ;; If the word begins with a bang (!), this is a "not" - ;; spec. We put this spec (minus the bang) and the - ;; symbol `ignore' into the list. - (push (cons (concat - "^" (buffer-substring - (1+ (match-beginning 0)) - (match-end 0)) - "\\($\\|\\.\\)") - 'ignore) - out) - ;; There was no bang, so this is a "yes" spec. - (push (cons (concat "^" (match-string 0) "\\($\\|\\.\\)") - 'subscribe) - out)))) - - (setq gnus-newsrc-options-n out)))) - -(eval-and-compile - (defalias 'gnus-long-file-names - (if (fboundp 'msdos-long-file-names) - 'msdos-long-file-names - (lambda () t)))) + ;; We are only interested in "options -n" lines - we + ;; ignore the other option lines. + (setq gnus-newsrc-options-n + (let (out) + (while (re-search-forward "[ \t]-n" nil t) + ;; Search for all "words"... + (while (re-search-forward + "[^ \t,\n]+" + (or (save-excursion + (and (re-search-forward "[ \t]-n" (point-at-eol) t) + (- (point) 2))) + (point-at-eol)) + t) + (if (eq (char-after (match-beginning 0)) ?!) + ;; If the word begins with a bang (!), this is a "not" + ;; spec. We put this spec (minus the bang) and the + ;; symbol `ignore' into the list. + (push (cons (concat + "^" (buffer-substring + (1+ (match-beginning 0)) + (match-end 0)) + "\\($\\|\\.\\)") + 'ignore) + out) + ;; There was no bang, so this is a "yes" spec. + (push (cons (concat "^" (match-string 0) "\\($\\|\\.\\)") + 'subscribe) + out)))) + out))) + (eval-and-compile + (defalias 'gnus-long-file-names + (if (fboundp 'msdos-long-file-names) + 'msdos-long-file-names + (lambda () t))))) (defvar gnus-save-newsrc-file-last-timestamp nil) (defun gnus-save-newsrc-file (&optional force) @@ -2734,12 +2728,10 @@ gnus-save-newsrc-file Use the group string names in `gnus-group-list' to pull info values from `gnus-newsrc-hashtb', and write a new value of `gnus-newsrc-alist'." - (when (and (or gnus-newsrc-alist gnus-killed-list) - gnus-current-startup-file) + (when (or gnus-newsrc-alist gnus-killed-list) ;; Save agent range limits for the currently active method. (when gnus-agent (gnus-agent-save-local force)) - (save-excursion (if (and (or gnus-use-dribble-file gnus-child) (not force) @@ -2750,97 +2742,33 @@ gnus-save-newsrc-file (gnus-run-hooks 'gnus-save-newsrc-hook) (if gnus-child (gnus-child-save-newsrc) - ;; Save .newsrc only if the select method is an NNTP method. - ;; The .newsrc file is for interoperability with other - ;; newsreaders, so saving non-NNTP groups there doesn't make - ;; much sense. - (when (and gnus-save-newsrc-file - (eq (car (gnus-server-to-method gnus-select-method)) - 'nntp)) - (gnus-message 8 "Saving %s..." gnus-current-startup-file) - (gnus-gnus-to-newsrc-format) - (gnus-message 8 "Saving %s...done" gnus-current-startup-file)) - - ;; Save .newsrc.eld. - (set-buffer (gnus-get-buffer-create " *Gnus-newsrc*")) - (setq-local version-control gnus-backup-startup-file) - (setq buffer-file-name - (concat gnus-current-startup-file ".eld")) - (setq default-directory (file-name-directory buffer-file-name)) - (buffer-disable-undo) - (erase-buffer) - (gnus-message 5 "Saving %s.eld..." gnus-current-startup-file) - - ;; Check timestamp of `gnus-current-startup-file'.eld against - ;; `gnus-save-newsrc-file-last-timestamp'. - (if (let* ((checkfile (concat gnus-current-startup-file ".eld")) - (mtime (file-attribute-modification-time - (file-attributes checkfile)))) - (and gnus-save-newsrc-file-last-timestamp - (time-less-p gnus-save-newsrc-file-last-timestamp - mtime) + ;; save .newsrc + (when gnus-save-dot-newsrc + (gnus-gnus-to-newsrc-format)) + ;; save .newsrc.eld + (gnus-message 5 "Saving %s..." gnus-newsrc-file) + ;; Starting two gnusae inevitably yields this annoying question + ;; of whether to save over each other. + (let ((mtime (file-attribute-modification-time + (file-attributes gnus-newsrc-file)))) + (if (and gnus-save-newsrc-file-last-timestamp + (time-less-p gnus-save-newsrc-file-last-timestamp mtime) (not (y-or-n-p (format "%s was updated externally after %s, save?" - checkfile + gnus-newsrc-file (format-time-string - "%c" - gnus-save-newsrc-file-last-timestamp)))))) - (gnus-message - 4 "Didn't save %s: updated externally" - (concat gnus-current-startup-file ".eld")) - (if gnus-save-startup-file-via-temp-buffer - (let ((coding-system-for-write gnus-ding-file-coding-system) + "%c" + gnus-save-newsrc-file-last-timestamp))))) + (gnus-message 4 "Not saving %s" gnus-newsrc-file) + (with-temp-file gnus-newsrc-file + (let ((coding-system-for-write gnus-ding-file-coding-system) (standard-output (current-buffer))) - (gnus-gnus-to-quick-newsrc-format) - (gnus-run-hooks 'gnus-save-quick-newsrc-hook) - (save-buffer) - (setq gnus-save-newsrc-file-last-timestamp - (file-attribute-modification-time - (file-attributes buffer-file-name)))) - (let ((coding-system-for-write gnus-ding-file-coding-system) - (version-control gnus-backup-startup-file) - (startup-file (concat gnus-current-startup-file ".eld")) - (working-dir (file-name-directory gnus-current-startup-file)) - working-file - (i -1)) - ;; Generate the name of a non-existent file. - (while (progn (setq working-file - (format - (if (and (eq system-type 'ms-dos) - (not (gnus-long-file-names))) - "%s#%d.tm#" ; MSDOS limits files to 8+3 - "%s#tmp#%d") - working-dir (setq i (1+ i)))) - (file-exists-p working-file))) - - (unwind-protect - (with-file-modes (file-modes startup-file) - (gnus-with-output-to-file working-file - (gnus-gnus-to-quick-newsrc-format) - (gnus-run-hooks 'gnus-save-quick-newsrc-hook)) - - ;; These bindings will mislead the current buffer - ;; into thinking that it is visiting the startup - ;; file. - (let ((buffer-backed-up nil) - (buffer-file-name startup-file) - (file-precious-flag t)) - ;; Backup the current version of the startup file. - (backup-buffer) - - ;; Replace the existing startup file with the temp file. - (rename-file working-file startup-file t) - (setq gnus-save-newsrc-file-last-timestamp - (file-attribute-modification-time - (file-attributes startup-file))))) - (condition-case nil - (delete-file working-file) - (file-error nil))))) - - (gnus-kill-buffer (current-buffer)) - (gnus-message - 5 "Saving %s.eld...done" gnus-current-startup-file))) + (gnus-gnus-to-quick-newsrc-format))) + (setq gnus-save-newsrc-file-last-timestamp + (file-attribute-modification-time + (file-attributes gnus-newsrc-file))) + (gnus-message 5 "Saving %s...done" gnus-newsrc-file)))) (gnus-dribble-delete-file) (gnus-group-set-mode-line))))) @@ -2894,7 +2822,7 @@ gnus-gnus-to-quick-newsrc-format ;; compatible with older versions of Gnus. At some point, ;; if/when a new version of Gnus is released, stop doing ;; this and move the corresponding decode in - ;; `gnus-read-newsrc-el-file' into a conversion routine. + ;; `gnus-read-newsrc-file' into a conversion routine. (gnus-newsrc-alist (mapcar (lambda (info) (cons (encode-coding-string (car info) 'utf-8-emacs) @@ -2931,13 +2859,15 @@ gnus-strip-killed-list (nreverse olist))) (defun gnus-gnus-to-newsrc-format (&optional foreign-ok) + "Save NNTP state into the Usenet .newsrc file. +We save a .newsrc file for the benefit of other newsreaders, although +Gnus itself doesn't consult it in most cases." (interactive (list (gnus-y-or-n-p "write foreign groups too? "))) - ;; Generate and save the .newsrc file. - (with-current-buffer (create-file-buffer gnus-current-startup-file) + (with-current-buffer (create-file-buffer gnus-dot-newsrc) (let ((standard-output (current-buffer)) (groups (delete "dummy.group" (copy-sequence gnus-group-list))) info ranges range method) - (setq buffer-file-name gnus-current-startup-file) + (setq buffer-file-name gnus-dot-newsrc) (setq default-directory (file-name-directory buffer-file-name)) (buffer-disable-undo) (erase-buffer) @@ -2949,8 +2879,9 @@ gnus-gnus-to-newsrc-format (setq info (nth 1 (gnus-group-entry g-name))) ;; Maybe don't write foreign groups to .newsrc. (when (or (null (setq method (gnus-info-method info))) - (equal method "native") - (inline (gnus-server-equal method gnus-select-method)) + (and (stringp method) + (or (equal "native" method) + (eq 'nntp (car (gnus-server-to-method method))))) foreign-ok) (insert g-name (if (> (gnus-info-level info) gnus-level-subscribed) @@ -2981,14 +2912,11 @@ gnus-gnus-to-newsrc-format ;; delete the silly thing entirely first. but this fails to provide ;; such niceties as .newsrc~ creation. (if gnus-modtime-botch - (delete-file gnus-startup-file) + (delete-file gnus-dot-newsrc) (clear-visited-file-modtime)) - (gnus-run-hooks 'gnus-save-standard-newsrc-hook) (let ((coding-system-for-write 'raw-text)) (save-buffer)) (kill-buffer (current-buffer))))) - - ;;; ;;; Child functions. ;;; @@ -3007,22 +2935,17 @@ 'gnus-slave-mode-hook (defun gnus-child-save-newsrc () (with-current-buffer gnus-dribble-buffer - (with-file-modes (or (ignore-errors - (file-modes - (concat gnus-current-startup-file ".eld"))) + (with-file-modes (or (ignore-errors (file-modes gnus-newsrc-file)) (default-file-modes)) - (let ((child-name - (make-temp-file (concat gnus-current-startup-file "-child-")))) - (let ((coding-system-for-write gnus-ding-file-coding-system)) - (gnus-write-buffer child-name)))))) + (let ((child-name (make-temp-file (concat gnus-newsrc-file "-child-"))) + (coding-system-for-write gnus-ding-file-coding-system)) + (gnus-write-buffer child-name))))) (defun gnus-parent-read-child-newsrc () (let ((child-files - (directory-files - (file-name-directory gnus-current-startup-file) + (directory-files (file-name-directory gnus-newsrc-file) t (concat - "^" (regexp-quote - (file-name-nondirectory gnus-current-startup-file)) + "^" (regexp-quote (file-name-nondirectory gnus-newsrc-file)) ;; When the obsolete variables like ;; `gnus-slave-mode-hook' etc are removed, the "slave" ;; bit of this regexp should also be removed. @@ -3059,17 +2982,15 @@ gnus-parent-read-child-newsrc (gnus-dribble-touch) (gnus-message 7 "Reading child newsrcs...done")))) - ;;; ;;; Group description. ;;; (defun gnus-read-all-descriptions-files () - (let ((methods (cons gnus-select-method - (nconc - (when (gnus-archive-server-wanted-p) - (list "archive")) - gnus-secondary-select-methods)))) + (let ((methods (nconc + (when (gnus-archive-server-wanted-p) + (list "archive")) + gnus-select-methods))) (while methods (gnus-read-descriptions-file (car methods)) (setq methods (cdr methods))) diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el index bcd76dda29..5dc8b46323 100644 --- a/lisp/gnus/gnus-sum.el +++ b/lisp/gnus/gnus-sum.el @@ -64,14 +64,12 @@ gnus-use-article-prefetch (require 'gnus-range) (require 'gnus-int) (require 'gnus-undo) -(require 'gnus-util) (require 'gmm-utils) (require 'mm-decode) (require 'shr) (require 'url) (require 'nnoo) -(eval-when-compile - (require 'subr-x)) +(require 'subr-x) (autoload 'gnus-summary-limit-include-cached "gnus-cache" nil '(gnus-summary-mode)) @@ -80,6 +78,7 @@ gnus-use-article-prefetch (autoload 'nnselect-article-rsv "nnselect" nil nil) (autoload 'nnselect-article-group "nnselect" nil nil) (autoload 'gnus-nnselect-group-p "nnselect" nil nil) +(autoload 'gnus-agent-get-undownloaded-list "gnus-agent") (defcustom gnus-kill-summary-on-exit t "If non-nil, kill the summary buffer when you exit from it. @@ -827,6 +826,7 @@ gnus-summary-mark-below score file." :group 'gnus-score-default :type 'integer) +(make-variable-buffer-local 'gnus-summary-mark-below) (defun gnus-widget-reversible-match (_widget value) "Ignoring WIDGET, convert VALUE to internal form. @@ -982,6 +982,7 @@ gnus-summary-expunge-below :group 'gnus-score-default :type '(choice (const :tag "off" nil) integer)) +(make-variable-buffer-local 'gnus-summary-expunge-below) (defcustom gnus-thread-expunge-below nil "All threads that have a total score less than this variable will be expunged. @@ -993,6 +994,7 @@ gnus-thread-expunge-below :group 'gnus-score-default :type '(choice (const :tag "off" nil) integer)) +(make-variable-buffer-local 'gnus-thread-expunge-below) (defcustom gnus-summary-menu-hook nil "Hook run after the creation of the summary mode menu." @@ -1321,6 +1323,7 @@ gnus-orphan-score :group 'gnus-score-default :type '(choice (const nil) integer)) +(make-variable-buffer-local 'gnus-orphan-score) (defcustom gnus-summary-save-parts-default-mime "image/.*" "A regexp to match MIME parts when saving multiple parts of a @@ -1378,37 +1381,35 @@ gnus-article-emulate-mime ;;; Internal variables (defvar gnus-summary-display-cache nil) -(defvar gnus-article-mime-handles nil) -(defvar gnus-article-decoded-p nil) -(defvar gnus-article-charset nil) +(defvar-local gnus-article-mime-handles nil) +(defvar-local gnus-article-decoded-p nil) +(defvar-local gnus-article-charset nil) (defvar gnus-article-ignored-charsets nil) -(defvar gnus-scores-exclude-files nil) -(defvar gnus-page-broken nil) - -(defvar gnus-original-article nil) +(defvar-local gnus-scores-exclude-files nil) (defvar gnus-article-internal-prepare-hook nil) -(defvar gnus-newsgroup-process-stack nil) +(defvar-local gnus-page-broken nil) + +(defvar-local gnus-newsgroup-process-stack nil) (defvar gnus-thread-indent-array nil) (defvar gnus-thread-indent-array-level gnus-thread-indent-level) -(defvar gnus-sort-gathered-threads-function #'gnus-thread-sort-by-number - "Function to sort articles within a thread after it has been gathered together.") +(make-obsolete-variable 'gnus-sort-gathered-threads-function nil "28.1") (defvar gnus-summary-save-parts-type-history nil) (defvar gnus-summary-save-parts-last-directory mm-default-directory) ;; Avoid highlighting in kill files. (defvar gnus-summary-inhibit-highlight nil) -(defvar gnus-newsgroup-selected-overlay nil) +(defvar-local gnus-newsgroup-selected-overlay nil) (defvar gnus-inhibit-limiting nil) -(defvar gnus-newsgroup-adaptive-score-file nil) -(defvar gnus-current-score-file nil) +(defvar-local gnus-newsgroup-adaptive-score-file nil) +(defvar-local gnus-current-score-file nil) (defvar gnus-current-move-group nil) (defvar gnus-current-copy-group nil) (defvar gnus-current-crosspost-group nil) -(defvar gnus-newsgroup-display nil) +(defvar-local gnus-newsgroup-display nil) -(defvar gnus-newsgroup-dependencies nil +(defvar-local gnus-newsgroup-dependencies (gnus-make-hashtable) "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 @@ -1417,9 +1418,9 @@ gnus-newsgroup-dependencies ;; dependency table using the message's Message-ID as the key. The ;; root key is the string "none". -(defvar gnus-newsgroup-adaptive nil) +(defvar-local gnus-newsgroup-adaptive gnus-use-adaptive-scoring) (defvar gnus-summary-display-article-function nil) -(defvar gnus-summary-highlight-line-function nil +(defvar-local gnus-summary-highlight-line-function nil "Function called after highlighting a summary line.") (defvar gnus-summary-line-format-alist @@ -1519,208 +1520,126 @@ gnus-last-shell-command (defvar gnus-newsgroup-agentized nil "Locally bound in each summary buffer to indicate if server has been agentized.") -(defvar gnus-newsgroup-begin nil) -(defvar gnus-newsgroup-end nil) -(defvar gnus-newsgroup-last-rmail nil) -(defvar gnus-newsgroup-last-mail nil) -(defvar gnus-newsgroup-last-folder nil) -(defvar gnus-newsgroup-last-file nil) -(defvar gnus-newsgroup-last-directory nil) -(defvar gnus-newsgroup-auto-expire nil) -(defvar gnus-newsgroup-active nil) -(defvar gnus-newsgroup-highest nil) - -(defvar gnus-newsgroup-data nil) -(defvar gnus-newsgroup-data-reverse nil) -(defvar gnus-newsgroup-limit nil) -(defvar gnus-newsgroup-limits nil) -(defvar gnus-summary-use-undownloaded-faces nil) - -(defvar gnus-newsgroup-unreads nil +(defvar-local gnus-newsgroup-begin nil) +(defvar-local gnus-newsgroup-end nil) +(defvar-local gnus-newsgroup-last-rmail nil) +(defvar-local gnus-newsgroup-last-mail nil) +(defvar-local gnus-newsgroup-last-folder nil) +(defvar-local gnus-newsgroup-last-file nil) +(defvar-local gnus-newsgroup-last-directory nil) +(defvar-local gnus-newsgroup-auto-expire nil) +(defvar-local gnus-newsgroup-active nil) +(defvar-local gnus-newsgroup-highest nil) + +(defvar-local gnus-newsgroup-data nil) +(defvar-local gnus-newsgroup-data-reverse nil) +(defvar-local gnus-newsgroup-limit nil) +(defvar-local gnus-newsgroup-limits nil) +(defvar-local gnus-summary-use-undownloaded-faces nil) + +(defvar-local gnus-newsgroup-unreads nil "Sorted list of unread articles in the current newsgroup.") -(defvar gnus-newsgroup-unselected nil +(defvar-local gnus-newsgroup-unselected nil "Sorted list of unselected unread articles in the current newsgroup.") -(defvar gnus-newsgroup-reads nil +(defvar-local gnus-newsgroup-reads nil "Alist of read articles and article marks in the current newsgroup.") -(defvar gnus-newsgroup-expunged-tally nil) +(defvar-local gnus-newsgroup-expunged-tally 0) -(defvar gnus-newsgroup-marked nil +(defvar-local gnus-newsgroup-marked nil "Sorted list of ticked articles in current newsgroup (a subset of unread art).") -(defvar gnus-newsgroup-spam-marked nil +(defvar-local gnus-newsgroup-spam-marked nil "List of ranges of articles that have been marked as spam.") -(defvar gnus-newsgroup-killed nil +(defvar-local gnus-newsgroup-killed nil "List of ranges of articles that have been through the scoring process.") -(defvar gnus-newsgroup-cached nil +(defvar-local gnus-newsgroup-cached nil "Sorted list of articles that come from the article cache.") -(defvar gnus-newsgroup-saved nil +(defvar-local gnus-newsgroup-saved nil "List of articles that have been saved.") -(defvar gnus-newsgroup-kill-headers nil) +(defvar-local gnus-newsgroup-kill-headers nil) -(defvar gnus-newsgroup-replied nil +(defvar-local gnus-newsgroup-replied nil "List of articles that have been replied to in the current newsgroup.") -(defvar gnus-newsgroup-forwarded nil +(defvar-local gnus-newsgroup-forwarded nil "List of articles that have been forwarded in the current newsgroup.") -(defvar gnus-newsgroup-expirable nil +(defvar-local gnus-newsgroup-expirable nil "Sorted list of articles in the current newsgroup that can be expired.") -(defvar gnus-newsgroup-processable nil +(defvar-local gnus-newsgroup-processable nil "List of articles in the current newsgroup that can be processed.") -(defvar gnus-newsgroup-downloadable nil +(defvar-local gnus-newsgroup-downloadable nil "Sorted list of articles in the current newsgroup that can be processed.") -(defvar gnus-newsgroup-unfetched nil +(defvar-local gnus-newsgroup-unfetched nil "Sorted list of articles in the current newsgroup whose headers have not been fetched into the agent. This list will always be a subset of gnus-newsgroup-undownloaded.") -(defvar gnus-newsgroup-undownloaded nil +(defvar-local gnus-newsgroup-undownloaded nil "List of articles in the current newsgroup that haven't been downloaded.") -(defvar gnus-newsgroup-unsendable nil +(defvar-local gnus-newsgroup-unsendable nil "List of articles in the current newsgroup that won't be sent.") -(defvar gnus-newsgroup-bookmarks nil +(defvar-local gnus-newsgroup-bookmarks nil "List of articles in the current newsgroup that have bookmarks.") -(defvar gnus-newsgroup-dormant nil +(defvar-local gnus-newsgroup-dormant nil "Sorted list of dormant articles in the current newsgroup.") -(defvar gnus-newsgroup-unseen nil +(defvar-local gnus-newsgroup-unseen nil "List of unseen articles in the current newsgroup.") -(defvar gnus-newsgroup-seen nil +(defvar-local gnus-newsgroup-seen nil "Range of seen articles in the current newsgroup.") -(defvar gnus-newsgroup-unexist nil +(defvar-local gnus-newsgroup-unexist nil "Range of unexisting articles in the current newsgroup.") -(defvar gnus-newsgroup-articles nil +(defvar-local gnus-newsgroup-articles nil "List of articles in the current newsgroup.") -(defvar gnus-newsgroup-scored nil +(defvar-local gnus-newsgroup-scored nil "List of scored articles in the current newsgroup.") -(defvar gnus-newsgroup-headers nil +(defvar-local gnus-newsgroup-headers nil "List of article headers in the current newsgroup.") -(defvar gnus-newsgroup-threads nil) +(defvar-local gnus-newsgroup-threads nil) -(defvar gnus-newsgroup-prepared nil +(defvar-local gnus-newsgroup-prepared nil "Whether the current group has been prepared properly.") -(defvar gnus-newsgroup-ancient nil +(defvar-local gnus-newsgroup-ancient nil "List of `gnus-fetch-old-headers' articles in the current newsgroup.") -(defvar gnus-newsgroup-sparse nil) +(defvar-local gnus-newsgroup-sparse nil) -(defvar gnus-newsgroup-selection nil) - -(defvar gnus-current-article nil) -(defvar gnus-article-current nil) -(defvar gnus-current-headers nil) -(defvar gnus-have-all-headers nil) -(defvar gnus-last-article nil) -(defvar gnus-newsgroup-history nil) -(defvar gnus-newsgroup-charset nil) +(defvar-local gnus-newsgroup-selection nil) +(defvar-local gnus-current-article nil) +(defvar-local gnus-article-current nil) +(defvar-local gnus-current-headers nil) +(defvar-local gnus-have-all-headers nil) +(defvar-local gnus-last-article nil) +(defvar-local gnus-newsgroup-history nil) +(defvar-local gnus-newsgroup-charset nil) (defvar gnus-newsgroup-ephemeral-charset nil) (defvar gnus-newsgroup-ephemeral-ignored-charsets nil) - (defvar gnus-article-before-search nil) -(defvar gnus-summary-local-variables - '(gnus-newsgroup-name - - ;; Marks lists - gnus-newsgroup-unreads - gnus-newsgroup-unselected - gnus-newsgroup-marked - gnus-newsgroup-spam-marked - gnus-newsgroup-reads - gnus-newsgroup-saved - gnus-newsgroup-replied - gnus-newsgroup-forwarded - gnus-newsgroup-expirable - gnus-newsgroup-killed - gnus-newsgroup-unseen - gnus-newsgroup-seen - gnus-newsgroup-unexist - gnus-newsgroup-cached - gnus-newsgroup-downloadable - gnus-newsgroup-undownloaded - gnus-newsgroup-unsendable - - gnus-newsgroup-selection - - gnus-newsgroup-begin gnus-newsgroup-end - gnus-newsgroup-last-rmail gnus-newsgroup-last-mail - gnus-newsgroup-last-folder gnus-newsgroup-last-file - gnus-newsgroup-last-directory - gnus-newsgroup-auto-expire - gnus-newsgroup-processable - gnus-newsgroup-unfetched - gnus-newsgroup-articles - gnus-newsgroup-bookmarks gnus-newsgroup-dormant - gnus-newsgroup-headers gnus-newsgroup-threads - gnus-newsgroup-prepared gnus-summary-highlight-line-function - gnus-current-article gnus-current-headers gnus-have-all-headers - gnus-last-article gnus-article-internal-prepare-hook - (gnus-summary-article-delete-hook . global) - (gnus-summary-article-move-hook . global) - gnus-newsgroup-dependencies gnus-newsgroup-selected-overlay - gnus-newsgroup-scored gnus-newsgroup-kill-headers - gnus-thread-expunge-below - gnus-score-alist gnus-current-score-file - (gnus-summary-expunge-below . global) - (gnus-summary-mark-below . global) - (gnus-orphan-score . global) - gnus-newsgroup-active gnus-scores-exclude-files - gnus-newsgroup-highest - gnus-newsgroup-history gnus-newsgroup-ancient - gnus-newsgroup-sparse gnus-newsgroup-process-stack - (gnus-newsgroup-adaptive . gnus-use-adaptive-scoring) - gnus-newsgroup-adaptive-score-file (gnus-reffed-article-number . -1) - (gnus-newsgroup-expunged-tally . 0) - gnus-cache-removable-articles - gnus-newsgroup-data gnus-newsgroup-data-reverse - gnus-newsgroup-limit gnus-newsgroup-limits - gnus-newsgroup-charset gnus-newsgroup-display - gnus-summary-use-undownloaded-faces) - "Variables that are buffer-local to the summary buffers.") - -(defvar gnus-newsgroup-variables nil - "A list of variables that have separate values in different newsgroups. -A list of newsgroup (summary buffer) local variables, or cons of -variables and their default expressions to be evalled (when the default -values are not nil), that should be made global while the summary buffer -is active. - -Note: The default expressions will be evaluated (using function `eval') -before assignment to the local variable rather than just assigned to it. -If the default expression is the symbol `global', that symbol will not -be evaluated but the global value of the local variable will be used -instead. - -These variables can be used to set variables in the group parameters -while still allowing them to affect operations done in other buffers. -For example: - -\(setq gnus-newsgroup-variables - \\='(message-use-followup-to - (gnus-visible-headers . - \"^From:\\\\|^Newsgroups:\\\\|^Subject:\\\\|^Date:\\\\|^To:\"))) -") +(make-obsolete-variable 'gnus-summary-local-variables nil "28.1") +(make-obsolete-variable 'gnus-newsgroup-variables nil "28.1") (eval-when-compile ;; Bind features so that require will believe that gnus-sum has @@ -1850,17 +1769,15 @@ gnus-simplify-buffer-fuzzy (defun gnus-simplify-subject-fuzzy (subject) "Simplify a subject string fuzzily. See `gnus-simplify-buffer-fuzzy' for details." - (save-excursion - (let ((regexp gnus-simplify-subject-fuzzy-regexp)) - (gnus-set-work-buffer) - (let ((case-fold-search t)) - ;; Remove uninteresting prefixes. - (when (and gnus-simplify-ignored-prefixes - (string-match gnus-simplify-ignored-prefixes subject)) - (setq subject (substring subject (match-end 0)))) - (insert subject) - (inline (gnus-simplify-buffer-fuzzy regexp)) - (buffer-string))))) + (gnus-with-temp-buffer + (let ((case-fold-search t)) + ;; Remove uninteresting prefixes. + (when (and gnus-simplify-ignored-prefixes + (string-match gnus-simplify-ignored-prefixes subject)) + (setq subject (substring subject (match-end 0)))) + (insert subject) + (gnus-simplify-buffer-fuzzy gnus-simplify-subject-fuzzy-regexp) + (buffer-string)))) (defsubst gnus-simplify-subject-fully (subject) "Simplify a subject string according to `gnus-summary-gather-subject-limit'." @@ -3119,11 +3036,6 @@ bookmark-make-record-function (defvar bidi-paragraph-direction) -(defvar gnus-summary-mode-group nil - "Variable for communication with `gnus-summary-mode'. -Allows the `gnus-newsgroup-name' local variable to be set before -the summary mode hooks are run.") - (define-derived-mode gnus-summary-mode gnus-mode "Summary" "Major mode for reading articles. \\ @@ -3143,10 +3055,6 @@ gnus-summary-mode \\{gnus-summary-mode-map}" :interactive nil - (let ((gnus-summary-local-variables gnus-newsgroup-variables)) - (gnus-summary-make-local-variables)) - (gnus-summary-make-local-variables) - (setq gnus-newsgroup-name gnus-summary-mode-group) (when (gnus-visual-p 'summary-menu 'menu) (gnus-summary-make-menu-bar) (gnus-summary-make-tool-bar)) @@ -3163,27 +3071,11 @@ gnus-summary-mode (make-local-variable 'gnus-summary-line-format-spec) (make-local-variable 'gnus-summary-dummy-line-format) (make-local-variable 'gnus-summary-dummy-line-format-spec) - (make-local-variable 'gnus-summary-mark-positions) - (make-local-variable 'gnus-article-buffer) - (make-local-variable 'gnus-article-current) - (make-local-variable 'gnus-original-article-buffer) - (add-hook 'pre-command-hook #'gnus-set-global-variables nil t) (mm-enable-multibyte) (setq-local bookmark-make-record-function - #'gnus-summary-bookmark-make-record)) - -(defun gnus-summary-make-local-variables () - "Make all the local summary buffer variables." - (dolist (local gnus-summary-local-variables) - (if (consp local) - (let ((global (if (eq (cdr local) 'global) - ;; Copy the global value of the variable. - (symbol-value (car local)) - ;; Use the value from the list. - (eval (cdr local) t)))) - (set (make-local-variable (car local)) global)) - ;; Simple nil-valued local variable. - (set (make-local-variable local) nil)))) + #'gnus-summary-bookmark-make-record) + (setq gnus-summary-buffer (current-buffer)) + (set-default 'gnus-summary-buffer gnus-summary-buffer)) ;; Summary data functions. @@ -3527,76 +3419,25 @@ gnus-summary-setup-buffer This function does all setup work that relies on the specific value of GROUP, and puts the buffer in `gnus-summary-mode'. -Returns non-nil if the setup was successful." - (let ((buffer (gnus-summary-buffer-name group)) - (dead-name (concat "*Dead Summary " group "*"))) - ;; If a dead summary buffer exists, we kill it. - (gnus-kill-buffer dead-name) - (if (get-buffer buffer) - (progn - (set-buffer buffer) - (setq gnus-summary-buffer (current-buffer)) - (not gnus-newsgroup-prepared)) - (set-buffer (gnus-get-buffer-create buffer)) - (setq gnus-summary-buffer (current-buffer)) - (let ((gnus-summary-mode-group group)) - (gnus-summary-mode)) - (when (gnus-group-quit-config group) - (setq-local gnus-single-article-buffer nil)) - (turn-on-gnus-mailing-list-mode) - ;; These functions don't currently depend on GROUP, but might in - ;; the future. - (gnus-update-format-specifications nil 'summary 'summary-mode 'summary-dummy) - (gnus-update-summary-mark-positions) - ;; Set any local variables in the group parameters. - (gnus-summary-set-local-parameters gnus-newsgroup-name) - t))) - -(defun gnus-set-global-variables () - "Set the global equivalents of the buffer-local variables. -They are set to the latest values they had. These reflect the summary -buffer that was in action when the last article was fetched." - (when (derived-mode-p 'gnus-summary-mode) - (setq gnus-summary-buffer (current-buffer)) - (let ((name gnus-newsgroup-name) - (marked gnus-newsgroup-marked) - (spam gnus-newsgroup-spam-marked) - (unread gnus-newsgroup-unreads) - (headers gnus-current-headers) - (data gnus-newsgroup-data) - (summary gnus-summary-buffer) - (article-buffer gnus-article-buffer) - (original gnus-original-article-buffer) - (gac gnus-article-current) - (reffed gnus-reffed-article-number) - (score-file gnus-current-score-file) - (default-charset gnus-newsgroup-charset) - vlist) - (dolist (local gnus-newsgroup-variables) - (push (eval (if (consp local) (car local) - local) - t) - vlist)) - (setq vlist (nreverse vlist)) - (with-temp-buffer - (setq gnus-newsgroup-name name - gnus-newsgroup-marked marked - gnus-newsgroup-spam-marked spam - gnus-newsgroup-unreads unread - gnus-current-headers headers - gnus-newsgroup-data data - gnus-article-current gac - gnus-summary-buffer summary - gnus-article-buffer article-buffer - gnus-original-article-buffer original - gnus-reffed-article-number reffed - gnus-current-score-file score-file - gnus-newsgroup-charset default-charset) - (dolist (local gnus-newsgroup-variables) - (set (if (consp local) - (car local) - local) - (pop vlist))))))) +Return non-nil if caller must prepare the summary buffer." + (gnus-kill-buffer (concat "*Dead Summary " group "*")) ;; kill deadened summaries + (let ((name (gnus-summary-buffer-name group))) + (if (gnus-buffer-live-p name) + (with-current-buffer name + (not gnus-newsgroup-prepared)) + (with-current-buffer (gnus-get-buffer-create name) + (gnus-summary-mode) + (setq gnus-newsgroup-name group) + (when (gnus-group-quit-config gnus-newsgroup-name) + (set (make-local-variable 'gnus-single-article-buffer) nil)) + (turn-on-gnus-mailing-list-mode) + ;; These functions don't currently depend on GROUP, but might in + ;; the future. + (gnus-update-format-specifications 'summary 'summary-mode 'summary-dummy) + (gnus-update-summary-mark-positions) + ;; Set any local variables in the group parameters. + (gnus-summary-set-local-parameters gnus-newsgroup-name) + t)))) (defun gnus-summary-article-unread-p (article) "Say whether ARTICLE is unread or not." @@ -3619,7 +3460,7 @@ gnus--dummy-mail-header (make-full-mail-header 0 "" "" "05 Apr 2001 23:33:09 +0400" "" "" 0 0 "" nil)) (defconst gnus--dummy-data-list - (list (gnus-data-make 0 nil nil gnus--dummy-mail-header nil))) + (list (gnus-data-make 0 nil 0 gnus--dummy-mail-header nil))) (defun gnus-make-thread-indent-array (&optional n) (when (or n @@ -3635,63 +3476,57 @@ gnus-make-thread-indent-array (defun gnus-update-summary-mark-positions () "Compute where the summary marks are to go." - (save-excursion - (when (gnus-buffer-live-p gnus-summary-buffer) - (set-buffer gnus-summary-buffer)) - (let ((spec gnus-summary-line-format-spec) - pos) - (save-excursion - (gnus-set-work-buffer) - (let ((gnus-tmp-unread ?Z) - (gnus-replied-mark ?Z) - (gnus-score-below-mark ?Z) - (gnus-score-over-mark ?Z) - (gnus-undownloaded-mark ?Z) - (gnus-summary-line-format-spec spec) - ;; Make sure `gnus-data-find' finds a dummy element - ;; so we don't call gnus-data- accessors on nil. - (gnus-newsgroup-data gnus--dummy-data-list) - (gnus-newsgroup-downloadable '(0)) - (gnus-visual nil) - case-fold-search ignores) - ;; Here, all marks are bound to Z. - (gnus-summary-insert-line gnus--dummy-mail-header - 0 nil t gnus-tmp-unread t nil "" nil 1) - (goto-char (point-min)) - ;; Memorize the positions of the same characters as dummy marks. - (while (re-search-forward "[A-D]" nil t) - (push (point) ignores)) - (erase-buffer) - ;; We use A-D as dummy marks in order to know column positions - ;; where marks should be inserted. - (setq gnus-tmp-unread ?A - gnus-replied-mark ?B - gnus-score-below-mark ?C - gnus-score-over-mark ?C - gnus-undownloaded-mark ?D) - (gnus-summary-insert-line gnus--dummy-mail-header - 0 nil t gnus-tmp-unread t nil "" nil 1) - ;; Ignore characters which aren't dummy marks. - (dolist (p ignores) - (delete-region (goto-char (1- p)) p) - (insert ?Z)) - (goto-char (point-min)) - (setq pos (list (cons 'unread - (and (search-forward "A" nil t) - (- (point) (point-min) 1))))) - (goto-char (point-min)) - (push (cons 'replied (and (search-forward "B" nil t) - (- (point) (point-min) 1))) - pos) - (goto-char (point-min)) - (push (cons 'score (and (search-forward "C" nil t) - (- (point) (point-min) 1))) - pos) - (goto-char (point-min)) - (push (cons 'download (and (search-forward "D" nil t) - (- (point) (point-min) 1))) - pos))) - (setq gnus-summary-mark-positions pos)))) + (with-current-buffer gnus-summary-buffer + (setq gnus-summary-mark-positions + (gnus-with-temp-buffer + (let ((gnus-tmp-unread ?Z) + (gnus-replied-mark ?Z) + (gnus-score-below-mark ?Z) + (gnus-score-over-mark ?Z) + (gnus-undownloaded-mark ?Z) + ;; Make sure `gnus-data-find' finds a dummy element + ;; so we don't call gnus-data- accessors on nil. + (gnus-newsgroup-data gnus--dummy-data-list) + (gnus-newsgroup-downloadable '(0)) + pos case-fold-search ignores gnus-visual) + ;; Here, all marks are bound to Z. + (gnus-summary-insert-line gnus--dummy-mail-header + 0 nil t gnus-tmp-unread t nil "" nil 1) + (goto-char (point-min)) + ;; Memorize the positions of the same characters as dummy marks. + (while (re-search-forward "[A-D]" nil t) + (push (point) ignores)) + (erase-buffer) + ;; We use A-D as dummy marks in order to know column positions + ;; where marks should be inserted. + (setq gnus-tmp-unread ?A + gnus-replied-mark ?B + gnus-score-below-mark ?C + gnus-score-over-mark ?C + gnus-undownloaded-mark ?D) + (gnus-summary-insert-line gnus--dummy-mail-header + 0 nil t gnus-tmp-unread t nil "" nil 1) + ;; Ignore characters which aren't dummy marks. + (dolist (p ignores) + (delete-region (goto-char (1- p)) p) + (insert ?Z)) + (goto-char (point-min)) + (setq pos (list (cons 'unread + (and (search-forward "A" nil t) + (- (point) (point-min) 1))))) + (goto-char (point-min)) + (push (cons 'replied (and (search-forward "B" nil t) + (- (point) (point-min) 1))) + pos) + (goto-char (point-min)) + (push (cons 'score (and (search-forward "C" nil t) + (- (point) (point-min) 1))) + pos) + (goto-char (point-min)) + (push (cons 'download (and (search-forward "D" nil t) + (- (point) (point-min) 1))) + pos) + pos))))) (defun gnus-summary-insert-dummy-line (subject number) "Insert a dummy root in the summary buffer." @@ -3841,12 +3676,13 @@ gnus-summary-insert-line (setq gnus-tmp-lines (if (= gnus-tmp-lines -1) "?" (number-to-string gnus-tmp-lines))) - (condition-case () + (condition-case err (put-text-property (point) (progn (eval gnus-summary-line-format-spec t) (point)) 'gnus-number gnus-tmp-number) - (error (gnus-message 5 "Error updating the summary line"))) + (error (gnus-message 3 "Error updating the summary line: %s" + (error-message-string err)))) (when (gnus-visual-p 'summary-highlight 'highlight) (forward-line -1) (gnus-summary-highlight-line) @@ -4030,168 +3866,156 @@ gnus-summary-read-group (defun gnus-summary-read-group-1 (group show-all no-article kill-buffer no-display &optional select-articles) - "Display articles and threads in a Summary buffer for GROUP." - ;; This function calls `gnus-summary-setup-buffer' to create the - ;; buffer, put it in `gnus-summary-mode', and set local variables; - ;; `gnus-select-newsgroup' to update the group's active and marks - ;; from the server; and `gnus-summary-prepare' to actually insert - ;; lines for articles. The rest of the function is mostly concerned - ;; with limiting and positioning and windowing and other visual - ;; effects. - - ;; Killed foreign groups can't be entered. - ;; (when (and (not (gnus-group-native-p group)) - ;; (not (gethash group gnus-newsrc-hashtb))) - ;; (error "Dead non-native groups can't be entered")) + "Display articles and threads in a Summary buffer for GROUP. + +Calls `gnus-summary-setup-buffer' to create the +buffer, put it in `gnus-summary-mode', and set local variables; +`gnus-select-newsgroup' to update the group's active and marks +from the server; and `gnus-summary-prepare' to actually insert +lines for articles. The rest of the function is mostly concerned +with limiting and positioning and windowing and other visual +effects." (gnus-message 7 "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 select-articles)))) - (cond - ;; This summary buffer exists already, so we just select it. - ((not new-group) - (gnus-set-global-variables) - (when kill-buffer - (gnus-kill-or-deaden-summary kill-buffer)) - (gnus-configure-windows 'summary 'force) - (gnus-set-mode-line 'summary) - (gnus-summary-position-point) - (message "") - t) - ;; We couldn't select this group. - ((null did-select) - (when (and (derived-mode-p 'gnus-summary-mode) - (not (equal (current-buffer) kill-buffer))) - (kill-buffer (current-buffer)) - (if (not quit-config) - (progn - ;; Update the info -- marks might need to be removed, - ;; for instance. - (gnus-summary-update-info) - (set-buffer gnus-group-buffer) - (gnus-group-jump-to-group group) - (gnus-group-next-unread-group 1)) - (gnus-handle-ephemeral-exit quit-config))) - (if (null (gnus-list-of-unread-articles group)) - (gnus-message 3 "Group %s contains no messages" group) - (gnus-message 3 "Can't select group")) - nil) - ;; The user did a `C-g' while prompting for number of articles, - ;; so we exit this group. - ((eq did-select 'quit) - (and (derived-mode-p 'gnus-summary-mode) - (not (equal (current-buffer) kill-buffer)) - (kill-buffer (current-buffer))) - (when kill-buffer - (gnus-kill-or-deaden-summary kill-buffer)) - (if (not quit-config) - (progn - (set-buffer gnus-group-buffer) - (gnus-group-jump-to-group group) - (gnus-configure-windows 'group 'force)) - (gnus-handle-ephemeral-exit quit-config)) - ;; Finally signal the quit. - (signal 'quit nil)) - ;; The group was successfully selected. - (t - (gnus-set-global-variables) - (when (boundp 'spam-install-hooks) - (spam-initialize)) - ;; Save the active value in effect when the group was entered. - (setq gnus-newsgroup-active - (copy-tree - (gnus-active gnus-newsgroup-name))) - (setq gnus-newsgroup-highest (cdr gnus-newsgroup-active)) - ;; You can change the summary buffer in some way with this hook. - (gnus-run-hooks 'gnus-select-group-hook) - (when (memq 'summary (gnus-update-format-specifications - nil 'summary 'summary-mode 'summary-dummy)) - ;; The format specification for the summary line was updated, - ;; so we need to update the mark positions as well. - (gnus-update-summary-mark-positions)) - ;; Do score processing. - (when gnus-use-scoring - (gnus-possibly-score-headers)) - ;; Check whether to fill in the gaps in the threads. - (when gnus-build-sparse-threads - (gnus-build-sparse-threads)) - ;; Find the initial limit. - (if show-all - (let ((gnus-newsgroup-dormant nil)) - (gnus-summary-initial-limit show-all)) - (gnus-summary-initial-limit show-all)) - ;; Generate the summary buffer. - (unless no-display - (gnus-summary-prepare)) - (when gnus-use-trees - (gnus-tree-open) ;Autoloaded from gnus-salt. - (declare-function gnus-tree-highlight-article "gnus-salt" (article face)) - (setq gnus-summary-highlight-line-function - #'gnus-tree-highlight-article)) - ;; If the summary buffer is empty, but there are some low-scored - ;; articles or some excluded dormants, we include these in the - ;; buffer. - (when (and (zerop (buffer-size)) - (not no-display)) - (cond (gnus-newsgroup-dormant - (gnus-summary-limit-include-dormant)) - ((and gnus-newsgroup-scored show-all) - (gnus-summary-limit-include-expunged t)))) - ;; Function `gnus-apply-kill-file' must be called in this hook. - (gnus-run-hooks 'gnus-apply-kill-hook) - (if (and (zerop (buffer-size)) - (not no-display)) - (progn - ;; This newsgroup is empty. - (gnus-summary-catchup-and-exit nil t) - (gnus-message 6 "No unread news") - (when kill-buffer - (gnus-kill-or-deaden-summary kill-buffer)) - ;; Return nil from this function. - nil) - ;; Hide conversation thread subtrees. We cannot do this in - ;; gnus-summary-prepare-hook since kill processing may not - ;; work with hidden articles. - (gnus-summary-maybe-hide-threads) - (gnus-configure-windows 'summary) - (when kill-buffer - (gnus-kill-or-deaden-summary kill-buffer)) - (gnus-summary-auto-select-subject) - ;; Don't mark any articles as selected if we haven't done that. - (when no-article - (setq overlay-arrow-position nil)) - ;; Show first unread article if requested. - (if (and (not no-article) - (not no-display) - gnus-newsgroup-unreads - gnus-auto-select-first) - (progn - (let ((art (gnus-summary-article-number))) - (when (and art - gnus-plugged - (not (memq art gnus-newsgroup-undownloaded)) - (not (memq art gnus-newsgroup-downloadable))) - (gnus-summary-goto-article art)))) - ;; Don't select any articles. - (gnus-summary-position-point) - (gnus-configure-windows 'summary 'force) - (gnus-set-mode-line 'summary)) - (when (and gnus-auto-center-group - (get-buffer-window gnus-group-buffer t)) - ;; Gotta use windows, because recenter does weird stuff if - ;; the current buffer ain't the displayed window. - (let ((owin (selected-window))) - (select-window (get-buffer-window gnus-group-buffer t)) - (when (gnus-group-goto-group group) - (recenter)) - (select-window owin))) - ;; Mark this buffer as "prepared". - (setq gnus-newsgroup-prepared t) - (gnus-run-hooks 'gnus-summary-prepared-hook) - (unless (gnus-ephemeral-group-p group) - (gnus-group-update-group group nil t)) - t))))) + (unwind-protect + (if-let ((prepare-p (gnus-summary-setup-buffer group))) + (with-current-buffer (gnus-summary-buffer-name group) + (cl-case (gnus-select-newsgroup group show-all select-articles) + ('quit + ;; The user did a `C-g' while prompting for number of articles, + ;; so we exit this group. + (and (derived-mode-p 'gnus-summary-mode) + (not (equal (current-buffer) kill-buffer)) + (kill-buffer (current-buffer))) + (when kill-buffer + (gnus-kill-or-deaden-summary kill-buffer)) + (if-let ((quit-config (gnus-group-quit-config group))) + (gnus-handle-ephemeral-exit quit-config) + (set-buffer gnus-group-buffer) + (gnus-group-jump-to-group group) + (gnus-configure-windows 'group 'force)) + ;; Finally signal the quit. + (signal 'quit nil)) + (nil + (when (and (derived-mode-p 'gnus-summary-mode) + (not (equal (current-buffer) kill-buffer))) + (kill-buffer (current-buffer)) + (if-let ((quit-config (gnus-group-quit-config group))) + (gnus-handle-ephemeral-exit quit-config) + (gnus-summary-update-info) ;; marks might need to be removed + (set-buffer gnus-group-buffer) + (gnus-group-jump-to-group group) + (gnus-group-next-unread-group 1))) + (if (null (gnus-list-of-unread-articles group)) + (gnus-message 3 "Group %s contains no messages" group) + (gnus-message 3 "Can't select group")) + nil) + (otherwise + ;; The group was successfully selected. + (when (boundp 'spam-install-hooks) + (spam-initialize)) + ;; Save the active value in effect when the group was entered. + (setq gnus-newsgroup-active + (copy-tree + (gnus-active gnus-newsgroup-name))) + (setq gnus-newsgroup-highest (cdr gnus-newsgroup-active)) + ;; You can change the summary buffer in some way with this hook. + (gnus-run-hooks 'gnus-select-group-hook) + (when (memq 'summary (gnus-update-format-specifications + 'summary 'summary-mode 'summary-dummy)) + ;; The format specification for the summary line was updated, + ;; so we need to update the mark positions as well. + (gnus-update-summary-mark-positions)) + ;; Do score processing. + (when gnus-use-scoring + (gnus-possibly-score-headers)) + ;; Check whether to fill in the gaps in the threads. + (when gnus-build-sparse-threads + (gnus-build-sparse-threads)) + ;; Find the initial limit. + (if show-all + (let ((gnus-newsgroup-dormant nil)) + (gnus-summary-initial-limit show-all)) + (gnus-summary-initial-limit show-all)) + ;; Generate the summary buffer. + (unless no-display + (gnus-summary-prepare)) + (when gnus-use-trees + (gnus-tree-open) ;Autoloaded from gnus-salt. + (declare-function gnus-tree-highlight-article "gnus-salt" (article face)) + (setq gnus-summary-highlight-line-function + #'gnus-tree-highlight-article)) + ;; If the summary buffer is empty, but there are some low-scored + ;; articles or some excluded dormants, we include these in the + ;; buffer. + (when (and (zerop (buffer-size)) + (not no-display)) + (cond (gnus-newsgroup-dormant + (gnus-summary-limit-include-dormant)) + ((and gnus-newsgroup-scored show-all) + (gnus-summary-limit-include-expunged t)))) + ;; Function `gnus-apply-kill-file' must be called in this hook. + (gnus-run-hooks 'gnus-apply-kill-hook) + (if (and (zerop (buffer-size)) + (not no-display)) + (progn + ;; This newsgroup is empty. + (gnus-summary-catchup-and-exit nil t) + (gnus-message 6 "No unread news") + (when kill-buffer + (gnus-kill-or-deaden-summary kill-buffer)) + ;; Return nil from this function. + nil) + ;; Hide conversation thread subtrees. We cannot do this in + ;; `gnus-summary-prepared-hook' since kill processing may not + ;; work with hidden articles. + (gnus-summary-maybe-hide-threads) + (when kill-buffer + (gnus-kill-or-deaden-summary kill-buffer)) + (gnus-summary-auto-select-subject) + ;; Don't mark any articles as selected if we haven't done that. + (when no-article + (setq overlay-arrow-position nil)) + ;; Show first unread article if requested. + (if (and (not no-article) + (not no-display) + gnus-newsgroup-unreads + gnus-auto-select-first) + (progn + (let ((art (gnus-summary-article-number))) + (when (and art + gnus-plugged + (not (memq art gnus-newsgroup-undownloaded)) + (not (memq art gnus-newsgroup-downloadable))) + (gnus-summary-goto-article art)))) + ;; Don't select any articles. + (gnus-summary-position-point) + (gnus-set-mode-line 'summary) + (save-excursion + (gnus-configure-windows 'summary 'force))) + (when (and gnus-auto-center-group + (get-buffer-window gnus-group-buffer t)) + ;; Gotta use windows, because recenter does weird stuff if + ;; the current buffer ain't the displayed window. + (let ((owin (selected-window))) + (select-window (get-buffer-window gnus-group-buffer t)) + (when (gnus-group-goto-group group) + (recenter)) + (select-window owin))) + ;; Mark this buffer as "prepared". + (setq gnus-newsgroup-prepared t) + (gnus-run-hooks 'gnus-summary-prepared-hook) + (unless (gnus-ephemeral-group-p group) + (gnus-group-update-group group nil t)) + t)))) + (with-current-buffer (gnus-summary-buffer-name group) + ;; Summary buffer already prepared, so we just select it. + (when kill-buffer + (gnus-kill-or-deaden-summary kill-buffer)) + (gnus-set-mode-line 'summary) + (gnus-summary-position-point) + (gnus-configure-windows 'summary 'force) + t)) + (gnus-message 7 "Retrieving newsgroup: %s...done" group))) (defun gnus-summary-auto-select-subject () "Select the subject line on initial group entry." @@ -4225,10 +4049,9 @@ gnus-summary-prepare (when gnus-newsgroup-headers (gnus-summary-prepare-threads (if gnus-show-threads - (gnus-sort-gathered-threads - (funcall gnus-summary-thread-gathering-function - (gnus-sort-threads - (gnus-cut-threads (gnus-make-threads))))) + (gnus-sort-threads + (funcall gnus-summary-thread-gathering-function + (gnus-cut-threads (gnus-make-threads)))) ;; Unthreaded display. (gnus-sort-articles gnus-newsgroup-headers)))) (setq gnus-newsgroup-data (nreverse gnus-newsgroup-data)) @@ -4317,7 +4140,7 @@ gnus-gather-threads-by-references (while threads (when (setq references (mail-header-references (caar threads))) (setq id (mail-header-id (caar threads)) - ids (inline (gnus-split-references references)) + ids (gnus-split-references references) entered nil) (while (setq ref (pop ids)) (setq ids (delete ref ids)) @@ -4346,16 +4169,6 @@ gnus-gather-threads-by-references (setq threads (cdr threads))) result)) -(defun gnus-sort-gathered-threads (threads) - "Sort subthreads inside each gathered thread by `gnus-sort-gathered-threads-function'." - (let ((result threads)) - (while threads - (when (stringp (caar threads)) - (setcdr (car threads) - (sort (cdar threads) gnus-sort-gathered-threads-function))) - (setq threads (cdr threads))) - result)) - (defun gnus-thread-loop-p (root thread) "Say whether ROOT is in THREAD." (let ((stack (list thread)) @@ -4698,33 +4511,42 @@ gnus-summary-update-article-line (cdr datal) (- (gnus-data-pos data) (gnus-data-pos (cadr datal)) inserted))))))) +(defmacro gnus-summary-assume-in-summary (&rest body) + "If we are not in an summary buffer, go there, and execute BODY. Restore." + (declare (indent 0) (debug t)) + `(save-current-buffer + (when (or (derived-mode-p 'gnus-summary-mode) + (when (gnus-buffer-live-p gnus-summary-buffer) + (set-buffer gnus-summary-buffer))) + ,@body))) + (defun gnus-summary-update-article (article &optional iheader) "Update ARTICLE in the summary buffer." - (set-buffer gnus-summary-buffer) - (let* ((header (gnus-summary-article-header article)) - (id (mail-header-id header)) - (data (gnus-data-find article)) - (thread (gnus-id-to-thread id)) - (references (mail-header-references header)) - (parent - (gnus-id-to-thread - (or (gnus-parent-id - (when (and references - (not (equal "" references))) - references)) - "none"))) - (inhibit-read-only t) - (old (car thread))) - (when thread - (unless 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. - (setf (gnus-data-number data) (gnus-id-to-article id)) - (setcar thread old) - nil)))) + (gnus-summary-assume-in-summary + (let* ((header (gnus-summary-article-header article)) + (id (mail-header-id header)) + (data (gnus-data-find article)) + (thread (gnus-id-to-thread id)) + (references (mail-header-references header)) + (parent + (gnus-id-to-thread + (or (gnus-parent-id + (when (and references + (not (equal "" references))) + references)) + "none"))) + (inhibit-read-only t) + (old (car thread))) + (when thread + (unless 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. + (setf (gnus-data-number data) (gnus-id-to-article id)) + (setcar thread old) + nil))))) (defun gnus-rebuild-thread (id &optional line) "Rebuild the thread containing ID. @@ -5694,7 +5516,6 @@ gnus-select-newsgroup gnus-summary-ignore-duplicates)) (info (nth 1 entry)) articles fetched-articles cached) - (unless (gnus-check-server (setq-local gnus-current-select-method (gnus-find-method-for-group group))) @@ -5707,12 +5528,10 @@ gnus-select-newsgroup (gnus-kill-buffer (current-buffer))) (error "Couldn't activate group %s: %s" group (gnus-status-message 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" group (gnus-status-message group))) - (when (and gnus-agent (gnus-active group)) (gnus-agent-possibly-alter-active group (gnus-active group) info) @@ -5721,11 +5540,9 @@ gnus-select-newsgroup (gnus-agent-find-parameter group 'agent-enable-undownloaded-faces))) - (setq gnus-newsgroup-name group gnus-newsgroup-unselected nil gnus-newsgroup-unreads (gnus-list-of-unread-articles group)) - (let ((display (gnus-group-find-parameter group 'display))) (setq gnus-newsgroup-display (cond @@ -5758,21 +5575,17 @@ gnus-select-newsgroup nil)))) (gnus-summary-setup-default-charset) - ;; Kludge to avoid having cached articles nixed out in virtual groups. (when (gnus-virtual-group-p group) (setq cached gnus-newsgroup-cached)) - (setq gnus-newsgroup-unreads (gnus-sorted-ndifference (gnus-sorted-ndifference gnus-newsgroup-unreads gnus-newsgroup-marked) gnus-newsgroup-dormant)) - (setq gnus-newsgroup-processable nil) (gnus-update-read-articles group gnus-newsgroup-unreads t) - ;; Adjust and set lists of article marks. (when info (gnus-adjust-marked-articles info)) @@ -5790,11 +5603,7 @@ gnus-select-newsgroup ;; Init the dependencies hash table. (setq gnus-newsgroup-dependencies (gnus-make-hashtable (length articles))) - (if (gnus-buffer-live-p gnus-group-buffer) - (gnus-set-global-variables) - (set-default 'gnus-newsgroup-name gnus-newsgroup-name)) ;; Retrieve the headers and read them in. - (setq gnus-newsgroup-headers (gnus-fetch-headers articles)) ;; Kludge to avoid having cached articles nixed out in virtual groups. @@ -5835,9 +5644,11 @@ gnus-select-newsgroup (and (gnus-group-auto-expirable-p group) (not (gnus-group-read-only-p group)))) ;; Set up the article buffer now, if necessary. - (unless (and gnus-single-article-buffer - (equal gnus-article-buffer "*Article*")) - (gnus-article-setup-buffer)) + (let ((single-article-p (and gnus-single-article-buffer + (equal gnus-article-buffer "*Article*")))) + (when (and (not single-article-p) + (gnus-buffer-live-p gnus-summary-buffer)))) + (gnus-article-setup-buffer) ;; First and last article in this newsgroup. (when gnus-newsgroup-headers (setq gnus-newsgroup-begin @@ -5859,7 +5670,6 @@ gnus-compute-unseen-list (declare-function gnus-get-predicate "gnus-agent" (predicate)) (defun gnus-summary-display-make-predicate (display) - (require 'gnus-agent) (when (= (length display) 1) (setq display (car display))) (unless gnus-summary-display-cache @@ -6032,7 +5842,7 @@ gnus-articles-to-read (defun gnus-killed-articles (killed articles) (let (out) (while articles - (when (inline (gnus-member-of-range (car articles) killed)) + (when (gnus-member-of-range (car articles) killed) (push (car articles) out)) (setq articles (cdr articles))) out)) @@ -6223,8 +6033,8 @@ gnus-update-marks (defun gnus-set-mode-line (where) "Set the mode line of the article or summary buffers. -If WHERE is `summary', the summary mode line format will be used." - ;; Is this mode line one we keep updated? +If WHERE is `summary', the summary mode line format will be used. +" (when (and (memq where gnus-updated-mode-lines) (symbol-value (intern (format "gnus-%s-mode-line-format-spec" where)))) @@ -6471,7 +6281,7 @@ gnus-get-newsgroup-headers-xover `nntp-retrieve-headers')." ;; Get the Xref when the users reads the articles since most/some ;; NNTP servers do not include Xrefs when using XOVER. - (setq gnus-article-internal-prepare-hook (list #'gnus-article-get-xrefs)) + (add-hook 'gnus-article-internal-prepare-hook 'gnus-article-get-xrefs nil t) (let ((mail-parse-charset gnus-newsgroup-charset) (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets) (cur nntp-server-buffer) @@ -7193,7 +7003,6 @@ gnus-summary-exit "Exit reading current newsgroup, and then return to group selection mode. `gnus-exit-group-hook' is called with no arguments if that value is non-nil." (interactive nil gnus-summary-mode) - (gnus-set-global-variables) (when (gnus-buffer-live-p gnus-article-buffer) (with-current-buffer gnus-article-buffer (mm-destroy-parts gnus-article-mime-handles) @@ -7254,8 +7063,7 @@ gnus-summary-exit (gnus-article-stop-animations) (unless leave-hidden (gnus-configure-windows 'group 'force)) - (if temporary - nil ;Nothing to do. + (unless temporary (set-buffer buf) (if (not gnus-kill-summary-on-exit) (progn @@ -7357,17 +7165,14 @@ gnus-handle-ephemeral-exit (unless (eq (cdr quit-config) 'group) (setq gnus-current-select-method (gnus-find-method-for-group gnus-newsgroup-name))) - (cond ((derived-mode-p 'gnus-summary-mode) - (gnus-set-global-variables)) - ((derived-mode-p 'gnus-article-mode) - (save-current-buffer - ;; The `gnus-summary-buffer' variable may point - ;; to the old summary buffer when using a single - ;; article buffer. - (unless (gnus-buffer-live-p gnus-summary-buffer) - (set-buffer gnus-group-buffer)) - (set-buffer gnus-summary-buffer) - (gnus-set-global-variables)))) + (when (derived-mode-p 'gnus-article-mode) + (save-current-buffer + ;; The `gnus-summary-buffer' variable may point + ;; to the old summary buffer when using a single + ;; article buffer. + (unless (gnus-buffer-live-p gnus-summary-buffer) + (set-buffer gnus-group-buffer)) + (set-buffer gnus-summary-buffer))) (if (or (eq (cdr quit-config) 'article) (eq (cdr quit-config) 'pick)) (if (and (boundp 'gnus-pick-mode) (symbol-value 'gnus-pick-mode)) @@ -7670,38 +7475,47 @@ gnus-summary-expand-window (defun gnus-summary-display-article (article &optional all-header) "Display ARTICLE in article buffer." - (unless (and (gnus-buffer-live-p gnus-article-buffer) - (with-current-buffer gnus-article-buffer - (derived-mode-p 'gnus-article-mode))) - (gnus-article-setup-buffer)) - (gnus-set-global-variables) - (with-current-buffer gnus-article-buffer - ;; The buffer may be non-empty and even narrowed, so go back to - ;; a sane state. - (widen) - ;; We're going to erase the buffer anyway so do it now: it can save us from - ;; uselessly performing multibyte-conversion of the current content. - (let ((inhibit-read-only t)) (erase-buffer)) - (setq gnus-article-charset gnus-newsgroup-charset) - (setq gnus-article-ignored-charsets gnus-newsgroup-ignored-charsets) - (mm-enable-multibyte)) - (if (null article) - nil - (prog1 - (funcall (or gnus-summary-display-article-function - #'gnus-article-prepare) - article all-header) - (gnus-run-hooks 'gnus-select-article-hook) - (when (and gnus-current-article - (not (zerop gnus-current-article))) - (gnus-summary-goto-subject gnus-current-article)) - (gnus-summary-recenter) - (when (and gnus-use-trees gnus-show-threads) - (gnus-possibly-generate-tree article) - (gnus-highlight-selected-tree article)) - ;; Successfully display article. - (gnus-article-set-window-start - (cdr (assq article gnus-newsgroup-bookmarks)))))) + (gnus-summary-assume-in-summary + (cl-block nil + (let ((result + (cond ((not article) + (gnus-message 3 "Article cannot be displayed") + (cl-return)) + (gnus-summary-display-article-function + (unless (and (gnus-buffer-live-p gnus-article-buffer) + (with-current-buffer gnus-article-buffer + (derived-mode-p 'gnus-article-mode))) + (gnus-article-setup-buffer)) + (with-current-buffer gnus-article-buffer + ;; The buffer may be non-empty and even narrowed, + ;; so go back to a sane state. + (widen) + ;; We're going to erase the buffer anyway so do it now: + ;; it can save us from uselessly performing + ;; multibyte-conversion of the current content. + (let ((inhibit-read-only t)) (erase-buffer)) + (setq gnus-article-charset gnus-newsgroup-charset) + (setq gnus-article-ignored-charsets + gnus-newsgroup-ignored-charsets) + (mm-enable-multibyte)) + (funcall gnus-summary-display-article-function + article all-header)) + (t (gnus-article-prepare article all-header))))) + (gnus-run-hooks 'gnus-select-article-hook) + (when (and gnus-current-article + (not (zerop gnus-current-article))) + (gnus-summary-goto-subject gnus-current-article)) + (gnus-summary-recenter) + (when (and gnus-use-trees gnus-show-threads) + (gnus-possibly-generate-tree article) + (gnus-highlight-selected-tree article)) + (with-current-buffer gnus-article-buffer + (unless gnus-article-decoded-p + (mm-disable-multibyte))) + ;; Successfully display article. + (gnus-article-set-window-start + (cdr (assq article gnus-newsgroup-bookmarks))) + result)))) (defun gnus-summary-select-article (&optional all-headers force pseudo article) "Select the current article. @@ -7709,50 +7523,40 @@ gnus-summary-select-article non-nil, the article will be re-fetched even if it already present in the article buffer. If PSEUDO is non-nil, pseudo-articles will also be displayed." - ;; Make sure we are in the summary buffer to work around bbdb bug. - (unless (derived-mode-p 'gnus-summary-mode) - (set-buffer gnus-summary-buffer)) - (let ((article (or article (gnus-summary-article-number))) - (all-headers (and all-headers t)) ; Must be t or nil. - gnus-summary-display-article-function) - (and (not pseudo) - (gnus-summary-article-pseudo-p article) - (error "This is a pseudo-article")) - (with-current-buffer gnus-summary-buffer + (gnus-summary-assume-in-summary + (let ((article (or article (gnus-summary-article-number))) + (all-headers (and all-headers t)) ; Must be t or nil. + gnus-summary-display-article-function) + (and (not pseudo) + (gnus-summary-article-pseudo-p article) + (error "This is a pseudo-article")) (if (or (and gnus-single-article-buffer - (or (null gnus-current-article) - (null gnus-article-current) - (null (get-buffer gnus-article-buffer)) - (not (eq article (cdr gnus-article-current))) - (not (equal (car gnus-article-current) - gnus-newsgroup-name)) - (not (get-buffer gnus-original-article-buffer)))) - (and (not gnus-single-article-buffer) - (or (null gnus-current-article) - (not (get-buffer gnus-original-article-buffer)) - (not (eq gnus-current-article article)))) - force) - ;; The requested article is different from the current article. - (progn - (gnus-summary-display-article article all-headers) - (when (gnus-buffer-live-p gnus-article-buffer) - (with-current-buffer gnus-article-buffer - (if (not gnus-article-decoded-p) ;; a local variable - (mm-disable-multibyte)))) - (gnus-article-set-window-start - (cdr (assq article gnus-newsgroup-bookmarks))) - article) - 'old)))) + (or (null gnus-current-article) + (null gnus-article-current) + (null (get-buffer gnus-article-buffer)) + (not (eq article (cdr gnus-article-current))) + (not (equal (car gnus-article-current) + gnus-newsgroup-name)) + (not (get-buffer gnus-original-article-buffer)))) + (and (not gnus-single-article-buffer) + (or (null gnus-current-article) + (not (get-buffer gnus-original-article-buffer)) + (not (eq gnus-current-article article)))) + force) + ;; The requested article is different from the current article. + (prog1 article + (gnus-summary-display-article article all-headers)) + 'old)))) (defun gnus-summary-force-verify-and-decrypt () "Display buttons for signed/encrypted parts and verify/decrypt them." (interactive nil gnus-summary-mode) (let ((mm-verify-option 'known) - (mm-decrypt-option 'known) - (gnus-article-emulate-mime t) - (gnus-buttonized-mime-types (append (list "multipart/signed" - "multipart/encrypted") - gnus-buttonized-mime-types))) + (mm-decrypt-option 'known) + (gnus-article-emulate-mime t) + (gnus-buttonized-mime-types (append (list "multipart/signed" + "multipart/encrypted") + gnus-buttonized-mime-types))) (gnus-summary-select-article nil 'force))) (defun gnus-summary-next-article (&optional unread subject backward push) @@ -7762,68 +7566,67 @@ gnus-summary-next-article If BACKWARD, the previous article is selected instead of the next." (interactive "P" gnus-summary-mode) ;; Make sure we are in the summary buffer. - (unless (derived-mode-p 'gnus-summary-mode) - (set-buffer gnus-summary-buffer)) - (cond - ;; Is there such an article? - ((and (gnus-summary-search-forward unread subject backward) - (or (gnus-summary-display-article (gnus-summary-article-number)) - (eq (gnus-summary-article-mark) gnus-canceled-mark))) - (gnus-summary-position-point)) - ;; If not, we try the first unread, if that is wanted. - ((and subject - gnus-auto-select-same - (gnus-summary-first-unread-article)) - (gnus-summary-position-point) - (gnus-message 6 "Wrapped")) - ;; Try to get next/previous article not displayed in this group. - ((and gnus-auto-extend-newsgroup - (not unread) (not subject)) - (gnus-summary-goto-article - (if backward (1- gnus-newsgroup-begin) (1+ gnus-newsgroup-end)) - nil (count-lines (point-min) (point)))) - ;; Go to next/previous group. - (t - (unless (gnus-ephemeral-group-p gnus-newsgroup-name) - (gnus-summary-jump-to-group gnus-newsgroup-name)) - (let ((cmd last-command-event) - (point - (with-current-buffer gnus-group-buffer - (point))) - (current-summary (current-buffer)) - (group - (if (eq gnus-keep-same-level 'best) - (gnus-summary-best-group gnus-newsgroup-name) - (gnus-summary-search-group backward gnus-keep-same-level)))) - ;; Select next unread newsgroup automagically. - (cond - ((or (not gnus-auto-select-next) - (not cmd)) - (unless (eq gnus-auto-select-next 'quietly) - (gnus-message 6 "No more%s articles" (if unread " unread" "")))) - ((or (eq gnus-auto-select-next 'quietly) - (and (eq gnus-auto-select-next 'slightly-quietly) - push) - (and (eq gnus-auto-select-next 'almost-quietly) - (gnus-summary-last-article-p))) - ;; Select quietly. - (if (gnus-ephemeral-group-p gnus-newsgroup-name) - (gnus-summary-exit) - (unless (eq gnus-auto-select-next 'quietly) - (gnus-message 6 "No more%s articles (%s)..." - (if unread " unread" "") - (if group (concat "selecting " group) - "exiting"))) - (gnus-summary-next-group nil group backward))) - (t - (when (numberp last-input-event) - ;; Somehow or other, we may now have selected a different - ;; window. Make point go back to the summary buffer. - (when (eq current-summary (current-buffer)) - ;; FIXME: This burps when get-buffer-window returns nil. - (select-window (get-buffer-window current-summary 0))) - (gnus-summary-walk-group-buffer - gnus-newsgroup-name cmd unread backward point)))))))) + (gnus-summary-assume-in-summary + (cond + ;; Is there such an article? + ((and (gnus-summary-search-forward unread subject backward) + (or (gnus-summary-display-article (gnus-summary-article-number)) + (eq (gnus-summary-article-mark) gnus-canceled-mark))) + (gnus-summary-position-point)) + ;; If not, we try the first unread, if that is wanted. + ((and subject + gnus-auto-select-same + (gnus-summary-first-unread-article)) + (gnus-summary-position-point) + (gnus-message 6 "Wrapped")) + ;; Try to get next/previous article not displayed in this group. + ((and gnus-auto-extend-newsgroup + (not unread) (not subject)) + (gnus-summary-goto-article + (if backward (1- gnus-newsgroup-begin) (1+ gnus-newsgroup-end)) + nil (count-lines (point-min) (point)))) + ;; Go to next/previous group. + (t + (unless (gnus-ephemeral-group-p gnus-newsgroup-name) + (gnus-summary-jump-to-group gnus-newsgroup-name)) + (let ((cmd last-command-event) + (point + (with-current-buffer gnus-group-buffer + (point))) + (current-summary (current-buffer)) + (group + (if (eq gnus-keep-same-level 'best) + (gnus-summary-best-group gnus-newsgroup-name) + (gnus-summary-search-group backward gnus-keep-same-level)))) + ;; Select next unread newsgroup automagically. + (cond + ((or (not gnus-auto-select-next) + (not cmd)) + (unless (eq gnus-auto-select-next 'quietly) + (gnus-message 6 "No more%s articles" (if unread " unread" "")))) + ((or (eq gnus-auto-select-next 'quietly) + (and (eq gnus-auto-select-next 'slightly-quietly) + push) + (and (eq gnus-auto-select-next 'almost-quietly) + (gnus-summary-last-article-p))) + ;; Select quietly. + (if (gnus-ephemeral-group-p gnus-newsgroup-name) + (gnus-summary-exit) + (unless (eq gnus-auto-select-next 'quietly) + (gnus-message 6 "No more%s articles (%s)..." + (if unread " unread" "") + (if group (concat "selecting " group) + "exiting"))) + (gnus-summary-next-group nil group backward))) + (t + (when (numberp last-input-event) + ;; Somehow or other, we may now have selected a different + ;; window. Make point go back to the summary buffer. + (when (eq current-summary (current-buffer)) + ;; FIXME: This burps when get-buffer-window returns nil. + (select-window (get-buffer-window current-summary 0))) + (gnus-summary-walk-group-buffer + gnus-newsgroup-name cmd unread backward point))))))))) (defun gnus-summary-walk-group-buffer (_from-group cmd unread backward start) (let ((keystrokes '((?\C-n (gnus-group-next-unread-group 1)) @@ -7909,7 +7712,6 @@ gnus-summary-next-page Also see the variable `gnus-article-skip-boring'." (interactive "P" gnus-summary-mode) - (gnus-set-global-variables) (let ((article (gnus-summary-article-number)) (article-window (get-buffer-window gnus-article-buffer t)) endp) @@ -9333,7 +9135,6 @@ gnus-summary-enter-digest-group params) ;; Couldn't select this doc group. (switch-to-buffer buf) - (gnus-set-global-variables) (gnus-configure-windows 'summary) (gnus-message 3 "Article couldn't be entered?")) (kill-buffer dig))))) @@ -10681,7 +10482,6 @@ gnus-summary-edit-article (with-current-buffer gnus-summary-buffer (let ((mail-parse-charset gnus-newsgroup-charset) (mail-parse-ignored-charsets gnus-newsgroup-ignored-charsets)) - (gnus-set-global-variables) (when (and (not force) (gnus-group-read-only-p)) (error "The current newsgroup does not support article editing")) @@ -11591,6 +11391,7 @@ gnus-summary-catchup gnus-catchup-mark) (while (gnus-summary-find-next (not all)) (gnus-summary-mark-article-as-read gnus-catchup-mark))) + (when (gnus-summary-first-subject (not all)) (while (and (if to-here (< (point) to-here) t) @@ -11639,8 +11440,8 @@ gnus-summary-catchup-and-exit (when (gnus-summary-catchup all quietly nil 'fast) ;; Select next newsgroup or exit. (if (and (not (gnus-group-quit-config gnus-newsgroup-name)) - (eq gnus-auto-select-next 'quietly)) - (gnus-summary-next-group nil) + (eq gnus-auto-select-next 'quietly)) + (gnus-summary-next-group nil) (gnus-summary-exit)))) (defun gnus-summary-catchup-all-and-exit (&optional quietly) @@ -12143,8 +11944,6 @@ gnus-summary-sort thread (lambda (t1 t2) (funcall thread t2 t1)))) - (gnus-sort-gathered-threads-function - gnus-thread-sort-functions) (gnus-article-sort-functions (if (not reverse) article @@ -12881,6 +12680,7 @@ gnus-offer-save-summaries (dolist (buffer (buffer-list)) (when (and (setq buffer (buffer-name buffer)) (string-match "Summary" buffer) + (/= (aref buffer 0) ? ) (with-current-buffer buffer ;; We check that this is, indeed, a summary buffer. (and (derived-mode-p 'gnus-summary-mode) diff --git a/lisp/gnus/gnus-topic.el b/lisp/gnus/gnus-topic.el index b974dff372..6ba2668e9d 100644 --- a/lisp/gnus/gnus-topic.el +++ b/lisp/gnus/gnus-topic.el @@ -25,14 +25,10 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'gnus) (require 'gnus-group) (require 'gnus-start) -(require 'gnus-util) -(eval-when-compile - (require 'subr-x)) +(require 'subr-x) (defgroup gnus-topic nil "Group topics." @@ -428,7 +424,7 @@ gnus-group-prepare-topics (and gnus-group-listed-groups (copy-sequence gnus-group-listed-groups)))) - (gnus-update-format-specifications nil 'topic) + (gnus-update-format-specifications 'topic) (when (or (not gnus-topic-alist) (not gnus-topology-checked-p)) diff --git a/lisp/gnus/gnus-undo.el b/lisp/gnus/gnus-undo.el index 64ed2bbad6..c7ba39ed24 100644 --- a/lisp/gnus/gnus-undo.el +++ b/lisp/gnus/gnus-undo.el @@ -43,7 +43,6 @@ ;;; Code: -(require 'gnus-util) (require 'gnus) (defgroup gnus-undo nil diff --git a/lisp/gnus/gnus-util.el b/lisp/gnus/gnus-util.el index be0284515d..d738614584 100644 --- a/lisp/gnus/gnus-util.el +++ b/lisp/gnus/gnus-util.el @@ -32,11 +32,10 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'seq) (require 'time-date) (require 'text-property-search) +(require 'cl-seq) (defcustom gnus-completing-read-function 'gnus-emacs-completing-read "Function use to do completing read." @@ -104,6 +103,9 @@ gnus-eval-in-buffer-window ,@forms) (select-window ,tempvar))))) +(defmacro gnus-push-end (elt place) + `(push ,elt (if (consp ,place) (cdr (last ,place)) ,place))) + (defsubst gnus-goto-char (point) (and point (goto-char point))) @@ -547,7 +549,7 @@ gnus-parent-id (when (and references (not (zerop (length references)))) (if n - (let ((ids (inline (gnus-split-references references)))) + (let ((ids (gnus-split-references references))) (while (nthcdr n ids) (setq ids (cdr ids))) (car ids)) @@ -555,10 +557,12 @@ gnus-parent-id (when (string-match "\\(<[^<]+>\\)[ \t]*\\'" references) (match-string 1 references)))))) -(defsubst gnus-buffer-live-p (buffer) +(defsubst gnus-buffer-live-p (buffer-or-name) "If BUFFER names a live buffer, return its object; else nil." - (and buffer (buffer-live-p (setq buffer (get-buffer buffer))) - buffer)) + (when-let* ((buffer-or-name buffer-or-name) + (buffer (get-buffer buffer-or-name))) + (when (buffer-live-p buffer) + buffer))) (define-obsolete-function-alias 'gnus-buffer-exists-p 'gnus-buffer-live-p "27.1") @@ -602,21 +606,7 @@ gnus-copy-file (setq to (read-file-name "Copy file to: " default-directory))) (copy-file file to)) -(defvar gnus-work-buffer " *gnus work*") - (declare-function gnus-get-buffer-create "gnus" (name)) -;; gnus.el requires mm-util. -(declare-function mm-enable-multibyte "mm-util") - -(defun gnus-set-work-buffer () - "Put point in the empty Gnus work buffer." - (if (get-buffer gnus-work-buffer) - (progn - (set-buffer gnus-work-buffer) - (erase-buffer)) - (set-buffer (gnus-get-buffer-create gnus-work-buffer)) - (kill-all-local-variables) - (mm-enable-multibyte))) (defmacro gnus-group-real-name (group) "Find the real name of a foreign newsgroup." @@ -749,14 +739,7 @@ gnus-delete-file (when (file-exists-p file) (delete-file file))) -(defun gnus-delete-duplicates (list) - "Remove duplicate entries from LIST." - (let ((result nil)) - (while list - (unless (member (car list) result) - (push (car list) result)) - (pop list)) - (nreverse result))) +(defalias 'gnus-delete-duplicates #'delete-dups) (defun gnus-delete-directory (directory) "Delete files in DIRECTORY. Subdirectories remain. @@ -1209,6 +1192,10 @@ gnus-string-equal (or (string-equal x y) (string-equal (downcase x) (downcase y))))) +(defmacro gnus-assign-former-global (var val buffer) + "Will rename this." + `(setf (buffer-local-value ,var ,buffer) ,val)) + (defcustom gnus-use-byte-compile t "If non-nil, byte-compile crucial run-time code." :type 'boolean @@ -1676,6 +1663,18 @@ gnus-kill-all-overlays (while overlays (delete-overlay (pop overlays))))) +(defmacro gnus-with-temp-buffer (&rest forms) + "Formerly gnus-set-work-buffer. Relay buffer-locals to temp buffer." + (declare (indent defun)) + `(let ((gnus-vars (cl-remove-if-not + (lambda (entry) + (zerop (or (cl-search "gnus-" (symbol-name (car entry))) + -1))) + (buffer-local-variables)))) + (with-temp-buffer + (mapc (lambda (v) (set (make-local-variable (car v)) (cdr v))) gnus-vars) + ,@forms))) + (provide 'gnus-util) ;;; gnus-util.el ends here diff --git a/lisp/gnus/gnus-uu.el b/lisp/gnus/gnus-uu.el index ceb2ebcdcb..4a2f957241 100644 --- a/lisp/gnus/gnus-uu.el +++ b/lisp/gnus/gnus-uu.el @@ -26,8 +26,6 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'gnus) (require 'gnus-art) (require 'message) diff --git a/lisp/gnus/gnus-win.el b/lisp/gnus/gnus-win.el index 8ac4e39fa5..7a799c1de6 100644 --- a/lisp/gnus/gnus-win.el +++ b/lisp/gnus/gnus-win.el @@ -24,11 +24,9 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'gnus) -(require 'gnus-util) -(require 'seq) + +(declare-function gnus-group-name-at-point "gnus-group") (defgroup gnus-windows nil "Window configuration." @@ -237,7 +235,28 @@ gnus-window-to-buffer-helper nil))) (defun gnus-configure-frame (split &optional window) - "Split WINDOW according to SPLIT." + "TODO: rewrite `gnus-win.el' to be less opinionated. + +It's not ideal to maintain hardcoded maps like `gnus-window-to-buffer' +and `gnus-buffer-configuration'." + (gnus-configure--frame + (progn + (when-let* ((what (cdr (assq (car split) gnus-window-to-buffer))) + (buf (gnus-window-to-buffer-helper what)) + (dead-buf (and (bufferp buf) (not (buffer-live-p buf))))) + (if-let* ((live-buf (gnus-buffer-live-p gnus-group-buffer)) + (group (with-current-buffer live-buf + (gnus-group-name-at-point)))) + (setcar split + (gnus-summary-buffer-name group)) + (error "No group at point"))) + split) + window)) + +(defun gnus-configure--frame (split &optional window) + "Split WINDOW according to SPLIT. + +Formerly `gnus-configure-frame'. Wasn't thread-safe." (let* ((current-window (or (get-buffer-window (current-buffer)) (selected-window))) (window (or window current-window))) @@ -263,7 +282,7 @@ gnus-configure-frame ;; This is a buffer to be selected. ((not (memq type '(frame horizontal vertical))) (let ((buffer (cond ((stringp type) type) - (t (cdr (assq type gnus-window-to-buffer)))))) + (t (cdr (assq type gnus-window-to-buffer)))))) (unless buffer (error "Invalid buffer type: %s" type)) (let ((buf (gnus-get-buffer-create @@ -374,9 +393,7 @@ gnus-frame-split-p (defun gnus-configure-windows (setting &optional force) (cond - ((null setting) - ;; Do nothing. - ) + ((null setting)) ((window-configuration-p setting) (set-window-configuration setting)) (t @@ -403,14 +420,14 @@ gnus-configure-windows (unless (gnus-buffer-live-p nntp-server-buffer) (nnheader-init-server-buffer)) - ;; Remove all 'window-atom parameters, as we're going to blast - ;; and recreate the window layout. - (when (window-parameter nil 'window-atom) - (let ((root (window-atom-root))) - (walk-window-subtree - (lambda (win) - (set-window-parameter win 'window-atom nil)) - root t))) + ;; Remove all 'window-atom parameters, as we're going to blast + ;; and recreate the window layout. + (when (window-parameter nil 'window-atom) + (let ((root (window-atom-root))) + (walk-window-subtree + (lambda (win) + (set-window-parameter win 'window-atom nil)) + root t))) ;; Either remove all windows or just remove all Gnus windows. (let ((frame (selected-frame))) @@ -434,11 +451,11 @@ gnus-configure-windows (gnus-configure-frame split) (run-hooks 'gnus-configure-windows-hook) - ;; If we're using atomic windows, and the current frame has - ;; multiple windows, make them atomic. - (when (and gnus-use-atomic-windows - (window-parent (selected-window))) - (window-make-atom (window-parent (selected-window)))) + ;; If we're using atomic windows, and the current frame has + ;; multiple windows, make them atomic. + (when (and gnus-use-atomic-windows + (window-parent (selected-window))) + (window-make-atom (window-parent (selected-window)))) (when gnus-window-frame-focus (select-frame-set-input-focus diff --git a/lisp/gnus/gnus.el b/lisp/gnus/gnus.el index 7dde799a5b..ac252a043b 100644 --- a/lisp/gnus/gnus.el +++ b/lisp/gnus/gnus.el @@ -6,7 +6,7 @@ ;; Author: Masanobu UMEDA ;; Lars Magne Ingebrigtsen ;; Keywords: news, mail -;; Version: 5.13 +;; Version: 5.14pre ;; This file is part of GNU Emacs. @@ -29,12 +29,11 @@ (run-hooks 'gnus-load-hook) -(eval-when-compile (require 'cl-lib) - (require 'subr-x)) +(require 'gnus-util) (require 'wid-edit) (require 'mm-util) (require 'nnheader) -(require 'seq) +(require 'subr-x) ;; These are defined afterwards with gnus-define-group-parameter (defvar gnus-ham-process-destinations) @@ -296,7 +295,7 @@ gnus-dbus "D-Bus integration for Gnus." :group 'gnus) -(defconst gnus-version-number "5.13" +(defconst gnus-version-number "5.14pre" "Version number for this version of Gnus.") (defconst gnus-version (format "Gnus v%s" gnus-version-number) @@ -850,9 +849,6 @@ gnus-splash-svg-color-symbols ;;; Do the rest. -(require 'gnus-util) -(require 'nnheader) - (defcustom gnus-parameters nil "Alist of group parameters. @@ -1024,35 +1020,29 @@ gnus-getenv-nntpserver ;; starting or even loading Gnus. ;;;###autoload(custom-autoload 'gnus-select-method "gnus") +(defvar gnus-secondary-select-methods) +(defvar gnus-select-methods) (defcustom gnus-select-method - (list 'nntp (or (gnus-getenv-nntpserver) - (when (and gnus-default-nntp-server - (not (string= gnus-default-nntp-server ""))) - gnus-default-nntp-server) - "news")) - "Default method for selecting a newsgroup. -This variable should be a list, where the first element is how the -news is to be fetched, the second is the address. - -For instance, if you want to get your news via \"flab.flab.edu\" using -NNTP, you could say: - -\(setq gnus-select-method \\='(nntp \"flab.flab.edu\")) - -If you want to use your local spool, say: - -\(setq gnus-select-method (list \\='nnspool (system-name))) - -If you use this variable, you must set `gnus-nntp-server' to nil. - -There is a lot more to know about select methods and virtual servers - -see the manual for details." - ;; Emacs has set-after since 22.1. - ;set-after '(gnus-default-nntp-server) + (if-let ((nntp (or (gnus-getenv-nntpserver) + (unless (zerop (length gnus-default-nntp-server)) + gnus-default-nntp-server)))) + `(nntp ,nntp) + '(nnnil "")) + "This variable is deprecated in favor of `gnus-select-methods'." :group 'gnus-server :group 'gnus-start :initialize 'custom-initialize-default + :set (lambda (symbol value) + (set-default symbol value) + (setq gnus-select-methods (cons value gnus-secondary-select-methods))) :type 'gnus-select-method) +(make-obsolete-variable 'gnus-select-method 'gnus-select-methods "28.1" 'set) +(add-variable-watcher + 'gnus-select-method + (lambda (symbol newval operation _where) + (pcase operation + ((or 'set 'let 'unlet) + (custom-set-variables `(,symbol (quote ,newval))))))) (defcustom gnus-message-archive-method "archive" "Method used for archiving messages you've sent. @@ -1119,16 +1109,67 @@ gnus-secondary-servers (make-obsolete-variable 'gnus-secondary-servers 'gnus-select-method "24.1") (defcustom gnus-secondary-select-methods nil - "A list of secondary methods that will be used for reading news. -This is a list where each element is a complete select method (see -`gnus-select-method'). - -If, for instance, you want to read your mail with the nnml back end, -you could set this variable: - -\(setq gnus-secondary-select-methods \\='((nnml \"\")))" + "This variable is deprecated in favor of `gnus-select-methods'." :group 'gnus-server + :set (lambda (symbol value) + (set-default symbol value) + (setq gnus-select-methods (cons gnus-select-method value))) + :type '(repeat gnus-select-method)) +(make-obsolete-variable 'gnus-secondary-select-methods 'gnus-select-methods "28.1" 'set) +(add-variable-watcher + 'gnus-secondary-select-methods + (lambda (symbol newval operation _where) + (pcase operation + ((or 'set 'let 'unlet) + (custom-set-variables `(,symbol (quote ,newval))))))) + +(defcustom gnus-select-methods (cons gnus-select-method gnus-secondary-select-methods) + "((BACKEND1 SERVER1) (BACKEND2 SERVER2) ... ) where BACKEND is a symbol, e.g., +nntp, and SERVER is a string, e.g., \"news.gmane.io\". + +For example, these settings specify gmane over nntp, and a home +dovecot imap server. + +Method: nntp +Server: \"news.gmane.io\" + +Method: nnimap +Server: \"dovecot\" +Options: +Variable: nnimap-address + Value: \"localhost\" +Variable: nnimap-stream + Value: network +Variable: nnimap-server-port + Value: 143 +Variable: nnimap-inbox + Value: \"INBOX\" + +Or equivalently, + +\(custom-set-variables \\=`(gnus-select-methods + \\='((nntp \"news.gmane.io\") + (nnimap \"dovecot\" + (nnimap-address \"localhost\") + (nnimap-stream network) + (nnimap-server-port 143) + (nnimap-inbox \"INBOX\"))))) +" + :group 'gnus-server + :initialize 'custom-initialize-default + :set (lambda (symbol value) + (unless (listp (car value)) + (setq value (list value))) + (set-default symbol value) + (setq gnus-select-method (car value)) + (setq gnus-secondary-select-methods (cdr value))) :type '(repeat gnus-select-method)) +(add-variable-watcher + 'gnus-select-methods + (lambda (symbol newval operation _where) + (pcase operation + ((or 'set 'let 'unlet) + (custom-set-variables `(,symbol (quote ,newval))))))) (defcustom gnus-local-domain nil "Local domain name without a host name. @@ -1396,7 +1437,7 @@ gnus-redefine-select-method-widget (intern (car entry)))) gnus-valid-select-methods) (symbol :tag "other")) - (string :tag "Address") + (string :tag "Server") (repeat :tag "Options" :inline t (list :format "%v" @@ -2309,8 +2350,8 @@ gnus-agent-method-p-cache (defvar gnus-agent-target-move-group-header "X-Gnus-Agent-Move-To") (defvar gnus-draft-meta-information-header "X-Draft-From") (defvar gnus-group-get-parameter-function #'gnus-group-get-parameter) -(defvar gnus-original-article-buffer " *Original Article*") -(defvar gnus-newsgroup-name nil) +(defvar-local gnus-original-article-buffer " *Original Article*") +(defvar-local gnus-newsgroup-name nil) (defvar gnus-ephemeral-servers nil) (defvar gnus-server-method-cache nil) (defvar gnus-extended-servers nil) @@ -2422,8 +2463,8 @@ gnus-info-nodes (gnus-tree-mode "(gnus)Tree Display")) "Alist of major modes and related Info nodes.") -(defvar gnus-summary-buffer "*Summary*") -(defvar gnus-article-buffer "*Article*") +(defvar-local gnus-summary-buffer nil) +(defvar-local gnus-article-buffer "*Article*") (defvar gnus-server-buffer "*Server*") (defvar gnus-child nil @@ -2487,7 +2528,7 @@ gnus-moderated-hashtb ;; Save window configuration. (defvar gnus-prev-winconf nil) -(defvar gnus-reffed-article-number nil) +(defvar-local gnus-reffed-article-number -1) (defvar gnus-dead-summary nil) @@ -2956,7 +2997,7 @@ gnus-continuum-version "Return VERSION as a floating point number." (unless version (setq version gnus-version)) - (when (or (string-match "^\\([^ ]+\\)? ?Gnus v?\\([0-9.]+\\)$" version) + (when (or (string-match "^\\([^ ]+\\)? ?Gnus v?\\([0-9.]+\\)\\S-*$" version) (string-match "^\\(.?\\)gnus-\\([0-9.]+\\)$" version)) (let ((alpha (and (match-beginning 1) (match-string 1 version))) (number (match-string 2 version)) @@ -3454,9 +3495,13 @@ gnus-group-prefixed-p "Return the prefix of the current group name." (< 0 (length (gnus-group-real-prefix group)))) -(defun gnus-summary-buffer-name (group) +(defun gnus-summary-buffer-name (group &optional canonical) "Return the summary buffer name of GROUP." - (concat "*Summary " group "*")) + (let ((name (concat "*Summary " group "*")) + (main-thread-p (eq (current-thread) main-thread))) + (if (or canonical main-thread-p) + name + (format " %s %s" (thread-name (current-thread)) name)))) (defun gnus-group-method (group) "Return the server or method used for selecting GROUP. @@ -3490,10 +3535,10 @@ gnus-native-method-p (defsubst gnus-secondary-method-p (method) "Return whether METHOD is a secondary select method." (let ((methods gnus-secondary-select-methods) - (gmethod (inline (gnus-server-get-method nil method)))) + (gmethod (gnus-server-get-method nil method))) (while (and methods (not (gnus-method-equal - (inline (gnus-server-get-method nil (car methods))) + (gnus-server-get-method nil (car methods)) gmethod))) (setq methods (cdr methods))) methods)) @@ -3870,7 +3915,7 @@ gnus-server-extend-method ;; "hello", and the select method is ("hello" (my-var "something")) ;; in the group "alt.alt", this will result in a new virtual server ;; called "hello+alt.alt". - (if (or (not (inline (gnus-similar-server-opened method))) + (if (or (not (gnus-similar-server-opened method)) (not (cddr method))) method (let ((address-slot @@ -3942,12 +3987,11 @@ gnus-find-method-for-group gnus-select-method (setq method (cond ((stringp method) - (inline (gnus-server-to-method method))) + (gnus-server-to-method method)) ((stringp (cadr method)) (or - (inline - (gnus-same-method-different-name method)) - (inline (gnus-server-extend-method group method)))) + (gnus-same-method-different-name method) + (gnus-server-extend-method group method))) (t method))) (cond ((equal (cadr method) "") @@ -4026,7 +4070,7 @@ gnus-read-method ((assoc method gnus-valid-select-methods) (let ((address (if (memq 'prompt-address (assoc method gnus-valid-select-methods)) - (read-string "Address: ") + (read-string "Server: ") ""))) (or (cadr (assoc (format "%s:%s" method address) open-servers)) (list (intern method) address)))) @@ -4160,14 +4204,6 @@ gnus startup level. If ARG is non-nil and not a positive number, Gnus will prompt the user for the name of an NNTP server to use." (interactive "P") - ;; When using the development version of Gnus, load the gnus-load - ;; file. - (unless (string-match "^Gnus" gnus-version) - (load "gnus-load" nil t)) - (unless (or (byte-code-function-p (symbol-function 'gnus)) - (subr-native-elisp-p (symbol-function 'gnus))) - (message "You should compile Gnus") - (sit-for 2)) (let ((gnus-action-message-log (list nil))) (gnus-1 arg dont-connect child) (gnus-final-warning))) diff --git a/lisp/gnus/legacy-gnus-agent.el b/lisp/gnus/legacy-gnus-agent.el index 4f800891b2..bc62349c2c 100644 --- a/lisp/gnus/legacy-gnus-agent.el +++ b/lisp/gnus/legacy-gnus-agent.el @@ -26,7 +26,6 @@ ;;; Code: (require 'gnus-start) -(require 'gnus-util) (require 'gnus-range) (require 'gnus-agent) diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el index 02db38725a..db996e8192 100644 --- a/lisp/gnus/message.el +++ b/lisp/gnus/message.el @@ -5119,6 +5119,7 @@ message-insert-canlock (autoload 'nnheader-get-report "nnheader") (declare-function gnus-setup-posting-charset "gnus-msg" (group)) +(declare-function gnus-msg-inherit-variables "gnus-msg" (source-buffer dest-buffer)) (defun message-send-news (&optional arg) (require 'gnus-msg) @@ -6649,8 +6650,7 @@ message-pop-to-buffer "Message already being composed; erase? ") (message nil)))) (error "Message being composed"))) - (funcall (or switch-function 'pop-to-buffer-same-window) - name) + (funcall (or switch-function 'pop-to-buffer-same-window) name) (set-buffer name)) (erase-buffer) (message-mode))) @@ -7299,7 +7299,9 @@ message-followup (setq subject (concat "Re: " (message-simplify-subject subject))) (widen)) - (message-pop-to-buffer (message-buffer-name "followup" from newsgroups)) + (let ((before-pop (current-buffer))) + (message-pop-to-buffer (message-buffer-name "followup" from newsgroups)) + (gnus-msg-inherit-variables before-pop (current-buffer))) (setq message-reply-headers (make-full-mail-header diff --git a/lisp/gnus/nnfolder.el b/lisp/gnus/nnfolder.el index 2de5b83a7b..53a7be4cd7 100644 --- a/lisp/gnus/nnfolder.el +++ b/lisp/gnus/nnfolder.el @@ -33,7 +33,6 @@ (require 'nnmail) (require 'nnoo) (require 'gnus) -(require 'gnus-util) (require 'gnus-range) ;; FIXME not explicitly used in this file. diff --git a/lisp/gnus/nnheader.el b/lisp/gnus/nnheader.el index 708887cb9c..43a86ba8e1 100644 --- a/lisp/gnus/nnheader.el +++ b/lisp/gnus/nnheader.el @@ -26,20 +26,13 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (defvar gnus-decode-encoded-word-function) (defvar gnus-decode-encoded-address-function) (defvar gnus-alter-header-function) - (defvar nnmail-extra-headers) (defvar gnus-newsgroup-name) (defvar jka-compr-compression-info-list) -;; Requiring `gnus-util' at compile time creates a circular -;; dependency between nnheader.el and gnus-util.el. -;;(eval-when-compile (require 'gnus-util)) - (require 'mail-utils) (require 'mm-util) (require 'gnus-util) @@ -559,11 +552,10 @@ nnheader-callback-function (autoload 'gnus-get-buffer-create "gnus") -(defun nnheader-init-server-buffer () - "Initialize the Gnus-backend communication buffer." - (unless (gnus-buffer-live-p nntp-server-buffer) - (setq nntp-server-buffer (gnus-get-buffer-create " *nntpd*"))) - (with-current-buffer nntp-server-buffer +(defsubst nnheader-prep-server-buffer (buffer) + "Refactor \"setting the table\" of BUFFER for `nnheader-init-server-buffer' and +`gnus-thread-body'." + (with-current-buffer buffer (erase-buffer) (mm-enable-multibyte) (kill-all-local-variables) @@ -571,6 +563,12 @@ nnheader-init-server-buffer (setq-local nntp-process-response nil) t)) +(defun nnheader-init-server-buffer () + "Initialize the Gnus-backend communication buffer." + (unless (gnus-buffer-live-p nntp-server-buffer) + (setq nntp-server-buffer (get-buffer-create " *nntpd*"))) + (nnheader-prep-server-buffer nntp-server-buffer)) + ;;; Various functions the backends use. (defun nnheader-file-error (file) diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el index 3e2a202a6c..fe0b79870b 100644 --- a/lisp/gnus/nnimap.el +++ b/lisp/gnus/nnimap.el @@ -26,21 +26,37 @@ ;;; Code: -(eval-when-compile - (require 'cl-lib) - (require 'subr-x)) - (require 'nnheader) -(require 'gnus-util) (require 'gnus) (require 'nnoo) (require 'netrc) (require 'utf7) (require 'nnmail) +(require 'cl-seq) (autoload 'auth-source-forget+ "auth-source") (autoload 'auth-source-search "auth-source") +(declare-function x-server-version "xfns.c" (&optional terminal)) + +(defmacro nnimap-with-context (buffer &rest body) + (declare (indent defun)) + `(with-current-buffer ,buffer ,@body)) + +(defmacro nnimap-for-process-buffers (b &rest body) + (declare (indent defun)) + `(cl-flet ((match + (buf) + (let* ((regex* (mapconcat + #'identity + (mapcar #'regexp-quote + (split-string nnimap--process-buffer-fmt "%s")) + ".*")) + (regex (format "^%s$" regex*))) + (string-match-p regex (buffer-name buf))))) + (dolist (,b (seq-filter #'match (gnus-buffers))) + ,@body))) + (nnoo-declare nnimap) (defvoo nnimap-address nil @@ -123,8 +139,6 @@ nnimap-streaming Switching this off will make nnimap slower, but it helps with some servers.") -(defvoo nnimap-connection-alist nil) - (defvoo nnimap-current-infos nil) (defvoo nnimap-namespace nil) @@ -174,7 +188,6 @@ nnimap-process (defvar nnimap-status-string "") (defvar nnimap-keepalive-timer nil) -(defvar nnimap-process-buffers nil) (cl-defstruct nnimap group process commands capabilities select-result newlinep server @@ -199,10 +212,29 @@ nnimap-quirks (defvar nnimap-inhibit-logging nil) +(defconst nnimap--process-buffer-fmt " *nnimap %s*") + +(defun nnimap-assert-context (&optional dont-assert) + (let ((result (cl-every (lambda (v) (and (boundp v) v)) + '(nnimap-address nnimap-server-port)))) + (prog1 result + (unless dont-assert + (cl-assert result))))) + +(defsubst nnimap-process-buffer-key () + (nnimap-assert-context) + (format nnimap--process-buffer-fmt + (mapconcat (apply-partially #'format "%s") + (list + (nnoo-current-server 'nnimap) + nnimap-address + nnimap-server-port) + " "))) + (defun nnimap-group-to-imap (group) "Convert Gnus group name to IMAP mailbox name." - (let* ((inbox (if nnimap-namespace - (substring nnimap-namespace 0 -1) nil))) + (let ((inbox (when nnimap-namespace + (substring nnimap-namespace 0 -1)))) (utf7-encode (cond ((or (not inbox) (string-equal group inbox)) @@ -210,10 +242,13 @@ nnimap-group-to-imap ((string-prefix-p "#" group) (substring group 1)) (t - (concat nnimap-namespace group))) t))) + (concat nnimap-namespace group))) + t))) -(defun nnimap-buffer () - (nnimap-find-process-buffer nntp-server-buffer)) +(defalias 'nnimap-buffer #'nnimap-process-buffer) +(defun nnimap-process-buffer () + + (nnimap-get-process-buffer (nnimap-process-buffer-key))) (defun nnimap-header-parameters () (let (params) @@ -233,10 +268,10 @@ nnimap-header-parameters (format "%s" (nreverse params)))) (deffoo nnimap-retrieve-headers (articles &optional group server _fetch-old) - (with-current-buffer nntp-server-buffer + (nnimap-with-context nntp-server-buffer (erase-buffer) (when (nnimap-change-group group server) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (erase-buffer) (nnimap-wait-for-response (nnimap-send-command @@ -248,8 +283,7 @@ nnimap-retrieve-headers (error "Server closed connection")) (nnimap-transform-headers) (nnheader-remove-cr-followed-by-lf)) - (insert-buffer-substring - (nnimap-find-process-buffer (current-buffer)))) + (insert-buffer-substring (nnimap-process-buffer))) 'headers)) (defun nnimap-transform-headers () @@ -366,32 +400,35 @@ nnimap-article-ranges result)) (mapconcat #'identity (nreverse result) ","))))) -(deffoo nnimap-open-server (server &optional defs no-reconnect) - (if (nnimap-server-opened server) - t - (unless (assq 'nnimap-address defs) - (setq defs (append defs (list (list 'nnimap-address server))))) - (nnoo-change-server 'nnimap server defs) - (if no-reconnect - (nnimap-find-connection nntp-server-buffer) - (or (nnimap-find-connection nntp-server-buffer) - (nnimap-open-connection nntp-server-buffer))))) - -(defun nnimap-make-process-buffer (buffer) - (with-current-buffer - (generate-new-buffer (format " *nnimap %s %s %s*" - nnimap-address nnimap-server-port - buffer)) - (mm-disable-multibyte) - (buffer-disable-undo) - (gnus-add-buffer) - (setq-local after-change-functions nil) ;FIXME: Why? - (setq-local nnimap-object - (make-nnimap :server (nnoo-current-server 'nnimap) - :initial-resync 0)) - (push (list buffer (current-buffer)) nnimap-connection-alist) - (push (current-buffer) nnimap-process-buffers) - (current-buffer))) +(deffoo nnimap-open-server (server &optional defs _no-reconnect) + "Context switch based on SERVER. + +If `nnoo-current-server-p' is false for SERVER, +`nnoo-change-server' replaces the current context in `nnoo-state-alist' +with DEFS. And does so for all parent classes of nnimap. + +This imagined necessity of a back-line assoc list called `nnoo-state-alist' +was of course another \"youthful indiscretion.\" He just had to augment +the key of the front-line assoc list to incorporate SERVER." + (nnoo-change-server 'nnimap server defs) + (nnimap-server-opened server)) + +(defun nnimap-make-process-buffer (server process-buffer-key) + (nnimap-assert-context) + (let ((nnimap-vars (cl-remove-if-not + (lambda (entry) + (zerop (or (cl-search "nnimap-" (symbol-name (car entry))) + -1))) + (buffer-local-variables)))) + (with-current-buffer (get-buffer-create process-buffer-key t) + (prog1 (current-buffer) + (mm-disable-multibyte) + (buffer-disable-undo) + (gnus-add-buffer) + (cl-assert (null after-change-functions)) + (mapc (lambda (v) (set (make-local-variable (car v)) (cdr v))) nnimap-vars) + (setq-local nnimap-object (make-nnimap :server server + :initial-resync 0)))))) (defvar auth-source-creation-prompts) @@ -418,173 +455,172 @@ nnimap-keepalive (let ((now (current-time)) ;; Set this so we don't wait for a response. (nnimap-streaming t)) - (dolist (buffer nnimap-process-buffers) - (when (buffer-live-p buffer) - (with-current-buffer buffer - (when (and nnimap-object - (nnimap-last-command-time nnimap-object) - (time-less-p - (cdr nnimap-keepalive-intervals) - (time-subtract - now - (nnimap-last-command-time nnimap-object)))) - (with-local-quit - (ignore-errors ;E.g. "buffer foo has no process". - (nnimap-send-command "NOOP"))))))))) - -(defun nnimap-open-connection (buffer) - ;; Be backwards-compatible -- the earlier value of nnimap-stream was - ;; `ssl' when nnimap-server-port was nil. Sort of. + (nnimap-for-process-buffers buffer + (with-current-buffer buffer + (when (and nnimap-object + (nnimap-last-command-time nnimap-object) + (time-less-p + (cdr nnimap-keepalive-intervals) + (time-subtract + now + (nnimap-last-command-time nnimap-object)))) + (with-local-quit + (ignore-errors (nnimap-send-command "NOOP")))))))) + +(defun nnimap-open-connection (process-buffer-key) + (nnimap-assert-context) (when (and nnimap-server-port (eq nnimap-stream 'undecided)) (setq nnimap-stream 'ssl)) (let ((stream (if (eq nnimap-stream 'undecided) (cl-loop for type in '(ssl network) - for stream = (let ((nnimap-stream type)) - (nnimap-open-connection-1 buffer)) - while (eq stream 'no-connect) - finally (return stream)) - (nnimap-open-connection-1 buffer)))) - (if (eq stream 'no-connect) - nil + for stream = (let ((nnimap-stream type)) + (nnimap-open-connection-1 process-buffer-key)) + while (eq stream 'no-connect) + finally (return stream)) + (nnimap-open-connection-1 process-buffer-key)))) + (unless (eq stream 'no-connect) stream))) -;; This is only needed for Windows XP or earlier -(defun nnimap-map-port (port) - (declare-function x-server-version "xfns.c" (&optional terminal)) - (if (and (eq system-type 'windows-nt) - (<= (car (x-server-version)) 5) - (equal port "imaps")) - "993" - port)) - -(defun nnimap-open-connection-1 (buffer) - (unless (or nnimap-keepalive-timer - (null nnimap-keepalive-intervals)) - (setq nnimap-keepalive-timer (run-at-time - (car nnimap-keepalive-intervals) - (car nnimap-keepalive-intervals) - #'nnimap-keepalive))) - (with-current-buffer (nnimap-make-process-buffer buffer) +(defun nnimap-open-connection-1 (process-buffer-key) + (nnimap-assert-context) + (setq nnimap-keepalive-timer + (or nnimap-keepalive-timer + (when nnimap-keepalive-intervals + (run-at-time + (car nnimap-keepalive-intervals) + (car nnimap-keepalive-intervals) + #'nnimap-keepalive)))) + ;; Assert commit f33a5dc no longer necessary + (when (eq system-type 'windows-nt) + (cl-assert (> (car (x-server-version)) 5))) + (with-current-buffer + (nnimap-make-process-buffer + (nnoo-current-server 'nnimap) + process-buffer-key) (let* ((coding-system-for-read 'binary) (coding-system-for-write 'binary) (ports - (cond - ((memq nnimap-stream '(network plain starttls)) - (nnheader-message 7 "Opening connection to %s..." - nnimap-address) - '("imap" "143")) - ((eq nnimap-stream 'shell) - (nnheader-message 7 "Opening connection to %s via shell..." - nnimap-address) - '("imap")) - ((memq nnimap-stream '(ssl tls)) - (nnheader-message 7 "Opening connection to %s via tls..." - nnimap-address) - '("imaps" "imap" "993" "143")) - (t - (error "Unknown stream type: %s" nnimap-stream)))) - login-result credentials) - (when nnimap-server-port - (push nnimap-server-port ports)) - (let* ((stream-list - (open-network-stream - "*nnimap*" (current-buffer) nnimap-address - (nnimap-map-port (car ports)) - :type nnimap-stream - :warn-unless-encrypted t - :return-list t - :shell-command nnimap-shell-program - :capability-command "1 CAPABILITY\r\n" - :always-query-capabilities t - :end-of-command "\r\n" - :success " OK " - :starttls-function - (lambda (capabilities) - (when (string-match-p "STARTTLS" capabilities) - "1 STARTTLS\r\n")))) - (stream (car stream-list)) - (props (cdr stream-list)) - (greeting (plist-get props :greeting)) - (capabilities (plist-get props :capabilities)) - (stream-type (plist-get props :type)) - (server (nnoo-current-server 'nnimap))) - (when (and stream (not (memq (process-status stream) '(open run)))) - (setq stream nil)) - - (when (eq (process-type stream) 'network) - ;; Use TCP-keepalive so that connections that pass through a NAT - ;; router don't hang when left idle. - (set-network-process-option stream :keepalive t)) - - (setf (nnimap-process nnimap-object) stream) - (setf (nnimap-stream-type nnimap-object) stream-type) - (if (not stream) - (progn - (nnheader-report 'nnimap "Unable to contact %s:%s via %s" - nnimap-address (car ports) nnimap-stream) - 'no-connect) - (set-process-query-on-exit-flag stream nil) - (if (not (string-match-p "[*.] \\(OK\\|PREAUTH\\)" greeting)) - (nnheader-report 'nnimap "%s" greeting) - ;; Store the greeting (for debugging purposes). - (setf (nnimap-greeting nnimap-object) greeting) - (setf (nnimap-capabilities nnimap-object) - (mapcar #'upcase - (split-string capabilities))) - (unless (string-match-p "[*.] PREAUTH" greeting) - (if (not (setq credentials - (if (eq nnimap-authenticator 'anonymous) - (list "anonymous" - (message-make-address)) - ;; Look for the credentials based on - ;; the virtual server name and the address - (nnimap-credentials - (gnus-delete-duplicates - (list server nnimap-address)) - ports - nnimap-user)))) - (setq nnimap-object nil) - (let ((nnimap-inhibit-logging t)) - (setq login-result - (nnimap-login (car credentials) (cadr credentials)))) - (if (car login-result) - (progn - ;; Save the credentials if a save function exists - ;; (such a function will only be passed if a new - ;; token was created). - (when (functionp (nth 2 credentials)) - (funcall (nth 2 credentials))) - ;; See if CAPABILITY is set as part of login - ;; response. - (dolist (response (cddr (nnimap-command "CAPABILITY"))) - (when (string= "CAPABILITY" (upcase (car response))) - (setf (nnimap-capabilities nnimap-object) - (mapcar #'upcase (cdr response))))) - (when (and nnimap-use-namespaces - (nnimap-capability "NAMESPACE")) - (erase-buffer) - (nnimap-wait-for-response (nnimap-send-command "NAMESPACE")) - (let ((response (nnimap-last-response-string))) - (when (string-match - "^\\*\\W+NAMESPACE\\W+((\"\\([^\"\n]+\\)\"\\W+\"\\(.\\)\"))\\W+" - response) - (setq nnimap-namespace (match-string 1 response)))))) - ;; If the login failed, then forget the credentials - ;; that are now possibly cached. - (dolist (host (list (nnoo-current-server 'nnimap) - nnimap-address)) - (dolist (port ports) - (auth-source-forget+ :host host :port port))) - (delete-process (nnimap-process nnimap-object)) - (setq nnimap-object nil)))) - (when nnimap-object - (when (nnimap-capability "QRESYNC") - (nnimap-command "ENABLE QRESYNC")) - (nnheader-message 7 "Opening connection to %s...done" - nnimap-address) - (nnimap-process nnimap-object)))))))) + `(,@(when nnimap-server-port (list nnimap-server-port)) + ,@(cl-remove-if + (apply-partially #'equal nnimap-server-port) + (cond + ((memq nnimap-stream '(network plain starttls)) + (nnheader-message 7 "Opening connection to %s..." + nnimap-address) + '("imap" 143)) + ((eq nnimap-stream 'shell) + (nnheader-message 7 "Opening connection to %s via shell..." + nnimap-address) + '("imap")) + ((memq nnimap-stream '(ssl tls)) + (nnheader-message 7 "Opening connection to %s via tls..." + nnimap-address) + '("imaps" "imap" 993 143)) + (t + (error "Unknown stream type: %s" nnimap-stream)))))) + login-result + credentials + (stream-list + (open-network-stream + (let ((muffs "[ \t\n\r*]+")) + (string-trim process-buffer-key muffs muffs)) + (current-buffer) + nnimap-address + (car ports) + :type nnimap-stream + :warn-unless-encrypted t + :return-list t + :shell-command nnimap-shell-program + :capability-command "1 CAPABILITY\r\n" + :always-query-capabilities t + :end-of-command "\r\n" + :success " OK " + :starttls-function + (lambda (capabilities) + (when (string-match-p "STARTTLS" capabilities) + "1 STARTTLS\r\n")))) + (stream (car stream-list)) + (props (cdr stream-list)) + (greeting (plist-get props :greeting)) + (capabilities (plist-get props :capabilities)) + (stream-type (plist-get props :type)) + (server (nnoo-current-server 'nnimap))) + (when (and stream (not (memq (process-status stream) '(open run)))) + (setq stream nil)) + + (when (eq (process-type stream) 'network) + ;; Use TCP-keepalive so that connections that pass through a NAT + ;; router don't hang when left idle. + (set-network-process-option stream :keepalive t)) + + (setf (nnimap-process nnimap-object) stream) + (setf (nnimap-stream-type nnimap-object) stream-type) + (if (not stream) + (prog1 'no-connect + (nnheader-report 'nnimap "Unable to contact %s:%s via %s" + nnimap-address (car ports) nnimap-stream)) + (set-process-query-on-exit-flag stream nil) + (set-process-thread stream nil) + (if (not (string-match-p "[*.] \\(OK\\|PREAUTH\\)" greeting)) + (nnheader-report 'nnimap "%s" greeting) + (setf (nnimap-greeting nnimap-object) greeting) + (setf (nnimap-capabilities nnimap-object) + (mapcar #'upcase + (split-string capabilities))) + (unless (string-match-p "[*.] PREAUTH" greeting) + (if (not (setq credentials + (if (eq nnimap-authenticator 'anonymous) + (list "anonymous" + (message-make-address)) + ;; Look for the credentials based on + ;; the virtual server name and the address + (nnimap-credentials + (gnus-delete-duplicates + (list server nnimap-address)) + ports + nnimap-user)))) + (setq nnimap-object nil) + (let ((nnimap-inhibit-logging t)) + (setq login-result + (nnimap-login (car credentials) (cadr credentials)))) + (if (car login-result) + (progn + ;; Save the credentials if a save function exists + ;; (such a function will only be passed if a new + ;; token was created). + (when (functionp (nth 2 credentials)) + (funcall (nth 2 credentials))) + ;; See if CAPABILITY is set as part of login + ;; response. + (dolist (response (cddr (nnimap-command "CAPABILITY"))) + (when (string= "CAPABILITY" (upcase (car response))) + (setf (nnimap-capabilities nnimap-object) + (mapcar #'upcase (cdr response))))) + (when (and nnimap-use-namespaces + (nnimap-capability "NAMESPACE")) + (erase-buffer) + (nnimap-wait-for-response (nnimap-send-command "NAMESPACE")) + (let ((response (nnimap-last-response-string))) + (when (string-match + "^\\*\\W+NAMESPACE\\W+((\"\\([^\"\n]+\\)\"\\W+\"\\(.\\)\"))\\W+" + response) + (setq nnimap-namespace (match-string 1 response)))))) + ;; If the login failed, then forget the credentials + ;; that are now possibly cached. + (dolist (host (list (nnoo-current-server 'nnimap) + nnimap-address)) + (dolist (port ports) + (auth-source-forget+ :host host :port port))) + (delete-process (nnimap-process nnimap-object)) + (setq nnimap-object nil)))) + (when nnimap-object + (when (nnimap-capability "QRESYNC") + (nnimap-command "ENABLE QRESYNC")) + (nnheader-message 7 "Opening connection to %s...done" + nnimap-address) + (nnimap-process nnimap-object))))))) (autoload 'rfc2104-hash "rfc2104") @@ -656,7 +692,7 @@ nnimap-find-parameter (deffoo nnimap-close-server (&optional server defs) (when (nnoo-change-server 'nnimap server defs) (ignore-errors - (delete-process (get-buffer-process (nnimap-buffer)))) + (delete-process (get-buffer-process (nnimap-process-buffer)))) (nnoo-close-server 'nnimap server) t)) @@ -664,16 +700,13 @@ nnimap-request-close t) (deffoo nnimap-server-opened (&optional server) - (and (nnoo-current-server-p 'nnimap server) - nntp-server-buffer - (gnus-buffer-live-p nntp-server-buffer) - (nnimap-find-connection nntp-server-buffer))) + (nnoo-current-server-p 'nnimap server)) (deffoo nnimap-status-message (&optional _server) nnimap-status-string) (deffoo nnimap-request-article (article &optional group server to-buffer) - (with-current-buffer nntp-server-buffer + (nnimap-with-context nntp-server-buffer (let ((result (nnimap-change-group group server)) parts structure) (when (stringp article) @@ -681,7 +714,7 @@ nnimap-request-article (when (and result article) (erase-buffer) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (erase-buffer) (when nnimap-fetch-partial-articles (nnimap-command "UID FETCH %d (BODYSTRUCTURE)" article) @@ -705,7 +738,7 @@ nnimap-request-article (deffoo nnimap-request-head (article &optional group server to-buffer) (when (nnimap-change-group group server) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (when (stringp article) (setq article (nnimap-find-article-by-message-id group server article))) (if (null article) @@ -721,11 +754,11 @@ nnimap-request-head (cons group article))))))) (deffoo nnimap-request-articles (articles &optional group server) - (with-current-buffer nntp-server-buffer + (nnimap-with-context nntp-server-buffer (let ((result (nnimap-change-group group server))) (when result (erase-buffer) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (erase-buffer) (when (nnimap-command (if (nnimap-ver4-p) @@ -733,7 +766,7 @@ nnimap-request-articles "UID FETCH %s RFC822.PEEK") (nnimap-article-ranges (gnus-compress-sequence articles))) (let ((buffer (current-buffer))) - (with-current-buffer nntp-server-buffer + (nnimap-with-context nntp-server-buffer (nnheader-insert-buffer-substring buffer) (nnheader-ms-strip-cr))) t)))))) @@ -873,14 +906,13 @@ nnimap-request-group (let ((result (nnimap-change-group ;; Don't SELECT the group if we're going to select it ;; later, anyway. - (if (and (not dont-check) - (assoc group nnimap-current-infos)) - nil + (when (or dont-check + (not (assoc group nnimap-current-infos))) group) server)) (info (when info (list info))) active) - (with-current-buffer nntp-server-buffer + (nnimap-with-context nntp-server-buffer (when result (when (or (not dont-check) (not (setq active @@ -902,7 +934,7 @@ nnimap-request-group (deffoo nnimap-request-group-scan (group &optional server info) (when (nnimap-change-group nil server) (let (marks high low) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (erase-buffer) (let ((group-sequence (nnimap-send-command "SELECT %S" (nnimap-group-to-imap group))) @@ -926,7 +958,7 @@ nnimap-request-group-scan (nth 3 (car marks))) 0) low (or (nth 4 (car marks)) uidnext 1))))) - (with-current-buffer nntp-server-buffer + (nnimap-with-context nntp-server-buffer (erase-buffer) (insert (format @@ -936,17 +968,17 @@ nnimap-request-group-scan (deffoo nnimap-request-create-group (group &optional server _args) (when (nnimap-change-group nil server) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (car (nnimap-command "CREATE %S" (nnimap-group-to-imap group)))))) (deffoo nnimap-request-delete-group (group &optional _force server) (when (nnimap-change-group nil server) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (car (nnimap-command "DELETE %S" (nnimap-group-to-imap group)))))) (deffoo nnimap-request-rename-group (group new-name &optional server) (when (nnimap-change-group nil server) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (nnimap-unselect-group) (car (nnimap-command "RENAME %S %S" (nnimap-group-to-imap group) (nnimap-group-to-imap new-name)))))) @@ -960,13 +992,13 @@ nnimap-unselect-group (deffoo nnimap-request-expunge-group (group &optional server) (when (nnimap-change-group group server) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (car (nnimap-command "EXPUNGE"))))) (defun nnimap-get-flags (spec) (let ((articles nil) elems end) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (erase-buffer) (nnimap-wait-for-response (nnimap-send-command "UID FETCH %s FLAGS" spec)) @@ -984,7 +1016,7 @@ nnimap-get-flags (deffoo nnimap-close-group (_group &optional server) (when (eq nnimap-expunge 'on-exit) (nnoo-change-server 'nnimap server nil) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (nnimap-command "EXPUNGE")))) (deffoo nnimap-request-move-article (article group server accept-form @@ -1000,7 +1032,7 @@ nnimap-request-move-article ;; easy way. (let ((message-id (message-field-value "message-id"))) (if internal-move-group - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (let* ((can-move (and (nnimap-capability "MOVE") (equal (nnimap-quirk "MOVE") "MOVE"))) (command (if can-move @@ -1067,7 +1099,7 @@ nnimap-process-expiry-targets (gnus-server-to-method (format "nnimap:%s" server)))) (and (nnimap-change-group group server) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (nnheader-message 7 "Expiring articles from %s: %s" group articles) (let ((can-move (and (nnimap-capability "MOVE") (equal (nnimap-quirk "MOVE") "MOVE")))) @@ -1112,7 +1144,7 @@ nnimap-process-expiry-targets (defun nnimap-find-expired-articles (group) (let ((cutoff (nnmail-expired-article-p group nil nil))) (when cutoff - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (let ((result (nnimap-command "UID SEARCH SENTBEFORE %s" @@ -1126,7 +1158,7 @@ nnimap-find-article-by-message-id &optional limit) "Search for message with MESSAGE-ID in GROUP from SERVER. If LIMIT, first try to limit the search to the N last articles." - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (erase-buffer) (let* ((change-group-result (nnimap-change-group group server nil t)) (number-of-article @@ -1156,7 +1188,7 @@ nnimap-find-article-by-message-id (defun nnimap-delete-article (articles) "Delete ARTICLES." - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (nnimap-command "UID STORE %s +FLAGS.SILENT (\\Deleted)" (nnimap-article-ranges articles)) (cond @@ -1208,13 +1240,13 @@ nnimap-request-update-group-status '((subscribe "SUBSCRIBE") (unsubscribe "UNSUBSCRIBE"))))) (when command - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (nnimap-command "%s %S" (cadr command) (nnimap-group-to-imap group))))))) (deffoo nnimap-request-set-mark (group actions &optional server) (when (nnimap-change-group group server) (let (sequence) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (erase-buffer) ;; Just send all the STORE commands without waiting for ;; response. If they're successful, they're successful. @@ -1259,7 +1291,7 @@ nnimap-request-accept-article sequence message) (nnimap-add-cr) (setq message (buffer-substring-no-properties (point-min) (point-max))) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (when (setq message (or (nnimap-process-quirk "OK Gimap " 'append message) message)) ;; If we have this group open read-only, then unselect it @@ -1392,14 +1424,14 @@ nnimap-get-responses (deffoo nnimap-request-list (&optional server) (when (nnimap-change-group nil server) - (with-current-buffer nntp-server-buffer + (nnimap-with-context nntp-server-buffer (erase-buffer) (let ((groups - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (nnimap-get-groups))) sequences responses) (when groups - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (setf (nnimap-group nnimap-object) nil) (dolist (group groups) (setf (nnimap-examined nnimap-object) group) @@ -1438,9 +1470,9 @@ nnimap-request-list (deffoo nnimap-request-newgroups (_date &optional server) (when (nnimap-change-group nil server) - (with-current-buffer nntp-server-buffer + (nnimap-with-context nntp-server-buffer (erase-buffer) - (dolist (group (with-current-buffer (nnimap-buffer) + (dolist (group (with-current-buffer (nnimap-process-buffer) (nnimap-get-groups))) (unless (assoc group nnimap-current-infos) ;; Insert dummy numbers here -- they don't matter. @@ -1448,9 +1480,8 @@ nnimap-request-newgroups t))) (deffoo nnimap-retrieve-group-data-early (server infos) - (when (and (nnimap-change-group nil server) - infos) - (with-current-buffer (nnimap-buffer) + (when (nnimap-change-group nil server) + (with-current-buffer (nnimap-process-buffer) (erase-buffer) (setf (nnimap-group nnimap-object) nil) (setf (nnimap-initial-resync nnimap-object) 0) @@ -1516,10 +1547,10 @@ nnimap-finish-retrieve-group-infos (when (and sequences (nnimap-change-group nil server t) ;; Check that the process is still alive. - (get-buffer-process (nnimap-buffer)) - (memq (process-status (get-buffer-process (nnimap-buffer))) + (get-buffer-process (nnimap-process-buffer)) + (memq (process-status (get-buffer-process (nnimap-process-buffer))) '(open run))) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) ;; Wait for the final data to trickle in. (when (nnimap-wait-for-response (if (eq (cadar sequences) 'qresync) (caar sequences) @@ -1535,7 +1566,7 @@ nnimap-finish-retrieve-group-infos (unless dont-insert ;; Finally, just return something resembling an active file in ;; the nntp buffer, so that the agent can save the info, too. - (with-current-buffer nntp-server-buffer + (nnimap-with-context nntp-server-buffer (erase-buffer) (dolist (info infos) (let* ((group (gnus-info-group info)) @@ -1870,8 +1901,20 @@ nnimap-parse-flags (setq articles nil)))) groups)) -(defun nnimap-find-process-buffer (buffer) - (cadr (assoc buffer nnimap-connection-alist))) +(defun nnimap-get-process-buffer (process-buffer-key) + (cl-flet ((get + (key) + (cl-find-if (lambda (b) + (equal key (buffer-name b))) + (gnus-buffers)))) + (let ((extant (get process-buffer-key))) + (when (and extant (not (get-buffer-process extant))) + (gnus-kill-buffer extant) + (setq extant nil)) + (or extant + (progn (nnimap-open-connection process-buffer-key) + (get process-buffer-key)) + (error "Cannot connect to %s" process-buffer-key))))) (deffoo nnimap-request-post (&optional _server) (setq nnimap-status-string "Read-only server") @@ -1888,7 +1931,7 @@ nnimap-request-thread (nnselect-search-thread header) (when (nnimap-change-group group server) (let* ((cmd (nnimap-make-thread-query header)) - (result (with-current-buffer (nnimap-buffer) + (result (with-current-buffer (nnimap-process-buffer) (nnimap-command "UID SEARCH %s" cmd)))) (when result (gnus-fetch-headers @@ -1915,7 +1958,7 @@ nnimap-change-group ((not group) t) (t - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (let ((result (nnimap-command "%s %S" (if read-only "EXAMINE" @@ -1926,17 +1969,18 @@ nnimap-change-group (nnimap-select-result nnimap-object) result) result))))))) -(defun nnimap-find-connection (buffer) - "Find the connection delivering to BUFFER." - (let ((entry (assoc buffer nnimap-connection-alist))) - (when entry - (if (and (buffer-live-p (cadr entry)) - (get-buffer-process (cadr entry)) - (memq (process-status (get-buffer-process (cadr entry))) - '(open run))) - (get-buffer-process (cadr entry)) - (setq nnimap-connection-alist (delq entry nnimap-connection-alist)) - nil)))) +(defun nnimap-find-connection (_buffer) + "Find the connection delivering to BUFFER. +Confusingly, BUFFER will always be `nntp-server-buffer', i.e.,\" *nntpd*\", +so `nnimap-connection-alist' will usually be of length 1, and look like, +\((# #)) + +Multiplexing of different imap servers is made possible because +`nnoo-change-server' deftly swaps out this associative pair with the +current imap source (only the XXXXXX string changes). + +This is all changing." + (cl-assert nil)) ;; Leave room for `open-network-stream' to issue a couple of IMAP ;; commands before nnimap starts. @@ -2012,8 +2056,8 @@ nnimap-wait-for-line (match-string 1)))) (defun nnimap-wait-for-response (sequence &optional messagep) - (let ((process (get-buffer-process (current-buffer))) - openp) + (let (openp + (process (get-buffer-process (current-buffer)))) (condition-case nil (progn (goto-char (point-max)) @@ -2140,7 +2184,7 @@ nnimap-fetch-inbox t)) (defun nnimap-split-incoming-mail () - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (let ((nnimap-incoming-split-list nil) (nnmail-split-methods (cond diff --git a/lisp/gnus/nnmail.el b/lisp/gnus/nnmail.el index bcf01cfa9e..d8c9d5005c 100644 --- a/lisp/gnus/nnmail.el +++ b/lisp/gnus/nnmail.el @@ -29,7 +29,6 @@ (require 'gnus) ; for macro gnus-kill-buffer, at least (require 'nnheader) (require 'message) -(require 'gnus-util) (require 'mail-source) (require 'mm-util) (require 'gnus-int) @@ -1624,7 +1623,7 @@ nnmail-cache-insert (insert id "\n")))))) (defun nnmail-cache-primary-mail-backend () - (let ((be-list (cons gnus-select-method gnus-secondary-select-methods)) + (let ((be-list gnus-select-methods) (be nil) (res nil) (get-new-mail nil)) diff --git a/lisp/gnus/nnmaildir.el b/lisp/gnus/nnmaildir.el index 4867455393..a2bc109673 100644 --- a/lisp/gnus/nnmaildir.el +++ b/lisp/gnus/nnmaildir.el @@ -50,7 +50,6 @@ (require 'nnheader) (require 'gnus) -(require 'gnus-util) (require 'gnus-range) (require 'gnus-start) (require 'gnus-int) diff --git a/lisp/gnus/nnrss.el b/lisp/gnus/nnrss.el index a40fa88631..4a796b1bc4 100644 --- a/lisp/gnus/nnrss.el +++ b/lisp/gnus/nnrss.el @@ -31,7 +31,6 @@ (require 'nnmail) (require 'message) (require 'mm-util) -(require 'gnus-util) (require 'time-date) (require 'rfc2231) (require 'mm-url) diff --git a/lisp/gnus/nntp.el b/lisp/gnus/nntp.el index 1fd2ed06eb..1050779cc4 100644 --- a/lisp/gnus/nntp.el +++ b/lisp/gnus/nntp.el @@ -27,8 +27,6 @@ (require 'nnheader) (require 'nnoo) -(require 'gnus-util) -(require 'gnus) (require 'gnus-group) ;; gnus-group-name-charset (nnoo-declare nntp) @@ -252,7 +250,6 @@ nntp-server-list-active-group ;;; Internal variables. -(defvoo nntp-retrieval-in-progress nil) (defcustom nntp-record-commands nil "If non-nil, nntp will record all commands in the \"*nntp-log*\" buffer." :type 'boolean) @@ -399,9 +396,8 @@ nntp-wait-for (defun nntp-kill-buffer (buffer) (when (buffer-live-p buffer) - (let ((process (get-buffer-process buffer))) - (when process - (delete-process process))) + (when-let ((process (get-buffer-process buffer))) + (delete-process process)) (kill-buffer buffer) (nnheader-init-server-buffer))) @@ -410,7 +406,7 @@ nntp-erase-buffer (with-current-buffer buffer (erase-buffer))) -(defsubst nntp-find-connection (buffer) +(defun nntp-find-connection (buffer) "Find the connection delivering to BUFFER." (let ((alist nntp-connection-alist) (buffer (if (stringp buffer) (get-buffer buffer) buffer)) @@ -432,9 +428,8 @@ nntp-find-connection-entry (defun nntp-find-connection-buffer (buffer) "Return the process connection buffer tied to BUFFER." - (let ((process (nntp-find-connection buffer))) - (when process - (process-buffer process)))) + (when-let ((process (nntp-find-connection buffer))) + (process-buffer process))) (defun nntp-retrieve-data (command address _port buffer &optional wait-for callback decode) @@ -528,8 +523,8 @@ nntp-send-command-nodelete (defun nntp-send-command-and-decode (wait-for &rest strings) "Send STRINGS to server and wait until WAIT-FOR returns." - (when (not (or nnheader-callback-function - nntp-inhibit-output)) + (when (and (not nnheader-callback-function) + (not nntp-inhibit-output)) (nntp-erase-buffer nntp-server-buffer)) (let* ((command (mapconcat #'identity strings " ")) (process (nntp-find-connection nntp-server-buffer)) @@ -734,32 +729,19 @@ nntp-retrieve-headers (deffoo nntp-retrieve-group-data-early (server infos) "Retrieve group info on INFOS." (nntp-with-open-group nil server - (let ((buffer (nntp-find-connection-buffer nntp-server-buffer))) - (unless infos - (with-current-buffer buffer - (setq nntp-retrieval-in-progress nil))) - (when (and buffer - infos - (with-current-buffer buffer - (not nntp-retrieval-in-progress))) - ;; The first time this is run, this variable is `try'. So we - ;; try. - (when (eq nntp-server-list-active-group 'try) - (nntp-try-list-active - (gnus-group-real-name (gnus-info-group (car infos))))) - (with-current-buffer buffer - (erase-buffer) - ;; Mark this buffer as "in use" in case we try to issue two - ;; retrievals from the same server. This shouldn't happen, - ;; so this is mostly a sanity check. - (setq nntp-retrieval-in-progress t) - (let ((nntp-inhibit-erase t) - (command (if nntp-server-list-active-group - "LIST ACTIVE" "GROUP"))) - (dolist (info infos) - (nntp-send-command - nil command (gnus-group-real-name (gnus-info-group info))))) - (length infos)))))) + (when-let ((buffer (nntp-find-connection-buffer nntp-server-buffer))) + (when (eq nntp-server-list-active-group 'try) ;; `try' is initial value + (nntp-try-list-active + (gnus-group-real-name (gnus-info-group (car infos))))) + (with-current-buffer buffer + (erase-buffer) + (let ((nntp-inhibit-erase t) + (command (if nntp-server-list-active-group + "LIST ACTIVE" "GROUP"))) + (dolist (info infos) + (nntp-send-command + nil command (gnus-group-real-name (gnus-info-group info))))) + (length infos))))) (deffoo nntp-finish-retrieve-group-infos (server infos count) (nntp-with-open-group nil server @@ -769,8 +751,6 @@ nntp-finish-retrieve-group-infos (car infos))) (received 0) (last-point 1)) - (with-current-buffer buf - (setq nntp-retrieval-in-progress nil)) (when (and buf count) (with-current-buffer buf @@ -815,14 +795,7 @@ nntp-retrieve-groups "Retrieve group info on GROUPS." (nntp-with-open-group nil server - (when (and (nntp-find-connection-buffer nntp-server-buffer) - (with-current-buffer - (nntp-find-connection-buffer nntp-server-buffer) - (if (not nntp-retrieval-in-progress) - t - (message "Warning: Refusing to do retrieval from %s because a retrieval is already happening" - server) - nil))) + (when (nntp-find-connection-buffer nntp-server-buffer) (catch 'done (save-excursion ;; Erase nntp-server-buffer before nntp-inhibit-erase. @@ -1235,8 +1208,7 @@ nntp-make-process-buffer nntp-process-callback nil nntp-process-to-buffer nil nntp-process-start-point nil - nntp-process-decode nil - nntp-retrieval-in-progress nil) + nntp-process-decode nil) (current-buffer))) (defun nntp-open-connection (buffer) @@ -1311,6 +1283,19 @@ nntp-open-connection (prog1 (caar (push (list process buffer nil) nntp-connection-alist)) (push process nntp-connection-list) + (with-current-buffer buffer + (add-hook 'kill-buffer-hook + (apply-partially + (lambda (buffer) + (when-let ((process + (car (nntp-find-connection-entry buffer)))) + (setq nntp-connection-list + (delq process nntp-connection-list)) + (setq nntp-connection-alist + (assq-delete-all process nntp-connection-alist)) + (ignore-errors (delete-process process)))) + buffer) + nil t)) (with-current-buffer pbuffer (nntp-read-server-type) (erase-buffer) diff --git a/lisp/gnus/nnvirtual.el b/lisp/gnus/nnvirtual.el index 03a0ff296f..7ee21c633a 100644 --- a/lisp/gnus/nnvirtual.el +++ b/lisp/gnus/nnvirtual.el @@ -32,9 +32,7 @@ (require 'nntp) (require 'nnheader) -(require 'gnus) (require 'nnoo) -(require 'gnus-util) (require 'gnus-start) (require 'gnus-sum) (require 'gnus-msg) diff --git a/lisp/gnus/nnweb.el b/lisp/gnus/nnweb.el index f08dc47e31..b8cfacb5d5 100644 --- a/lisp/gnus/nnweb.el +++ b/lisp/gnus/nnweb.el @@ -24,11 +24,8 @@ ;;; Code: -(eval-when-compile (require 'cl-lib)) - (require 'nnoo) (require 'message) -(require 'gnus-util) (require 'gnus) (require 'nnmail) (require 'mm-util) diff --git a/lisp/obsolete/nnir.el b/lisp/obsolete/nnir.el index 40a8ec57b9..51dd14de95 100644 --- a/lisp/obsolete/nnir.el +++ b/lisp/obsolete/nnir.el @@ -37,7 +37,7 @@ ;; The Lisp setup may involve setting a few variables and setting up the ;; search engine. You can define the variables in the server definition ;; like this : -;; (setq gnus-secondary-select-methods '( +;; (setq gnus-select-methods '( ;; (nnimap "" (nnimap-address "localhost") ;; (nnir-search-engine namazu) ;; ))) @@ -518,7 +518,7 @@ nnir-compose-result (autoload 'gnus-server-get-active "gnus-int") (autoload 'nnimap-change-group "nnimap") -(declare-function nnimap-buffer "nnimap" ()) +(declare-function nnimap-process-buffer "nnimap" ()) (declare-function nnimap-command "nnimap" (&rest args)) ;; imap interface @@ -547,7 +547,7 @@ nnir-run-imap (condition-case () (when (nnimap-change-group (gnus-group-short-name group) server) - (with-current-buffer (nnimap-buffer) + (with-current-buffer (nnimap-process-buffer) (message "Searching %s..." group) (let ((arts 0) (result (nnimap-command "UID SEARCH %s" diff --git a/lisp/org/ol-gnus.el b/lisp/org/ol-gnus.el index 2d51447e0c..d913e5fc02 100644 --- a/lisp/org/ol-gnus.el +++ b/lisp/org/ol-gnus.el @@ -32,7 +32,6 @@ ;;; Code: (require 'gnus-sum) -(require 'gnus-util) (require 'nnheader) (or (require 'nnselect nil t) ; Emacs >= 28 (require 'nnir nil t)) ; Emacs < 28 diff --git a/test/lisp/gnus/gnus-test-select-methods.el b/test/lisp/gnus/gnus-test-select-methods.el new file mode 100644 index 0000000000..6fdde070e5 --- /dev/null +++ b/test/lisp/gnus/gnus-test-select-methods.el @@ -0,0 +1,103 @@ +;;; gnus-test-select-methods.el -*- lexical-binding:t -*- + +;; Copyright (C) 2019 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Code: + +(require 'ert) +(require 'gnus) +(require 'gnus-int) +(require 'gnus-start) + +(eval-when-compile + (put 'gnus-secondary-select-methods 'byte-obsolete-variable nil) + (put 'gnus-select-method 'byte-obsolete-variable nil) + (put 'gnus-nntp-server 'byte-obsolete-variable nil)) + +(ert-deftest gnus-test-select-methods-basic () + "Customizing `gnus-select-method' and `gnus-secondary-select-methods' +also modifies `gnus-select-methods'." + (let (gnus-select-method + gnus-secondary-select-methods + gnus-select-methods + (test-methods '((nnnil) (nntp "flab.flab.edu")))) + (custom-set-variables `(gnus-select-method (quote ,(car test-methods))) + `(gnus-secondary-select-methods (quote ,(cdr test-methods)))) + (should (cl-every #'identity + (cl-mapcar #'gnus-methods-equal-p gnus-select-methods test-methods))) + (should (gnus-method-equal gnus-select-method (car gnus-select-methods))) + (should (cl-every #'identity + (cl-mapcar #'gnus-methods-equal-p gnus-secondary-select-methods + (cdr gnus-select-methods)))))) + +(ert-deftest gnus-test-select-methods-out-of-band () + "Hamfistedly setting, not customizing, `gnus-select-method' and +`gnus-secondary-select-methods' also modifies `gnus-select-methods'." + (let (gnus-select-method + gnus-secondary-select-methods + gnus-select-methods + (test-methods '((nnnil) (nntp "flab.flab.edu")))) + (setq gnus-select-method (car test-methods) + gnus-secondary-select-methods (cdr test-methods)) + (should (cl-every #'identity + (cl-mapcar #'gnus-methods-equal-p gnus-select-methods test-methods))) + (should (gnus-method-equal gnus-select-method (car gnus-select-methods))) + (should (cl-every #'identity + (cl-mapcar #'gnus-methods-equal-p gnus-secondary-select-methods + (cdr gnus-select-methods)))))) + +(ert-deftest gnus-test-select-methods-override () + "Customizing `gnus-select-methods' overrides earlier customizations +of `gnus-select-method' and `gnus-secondary-select-methods'." + (let (gnus-select-method + gnus-secondary-select-methods + gnus-select-methods + (test-methods '((nnnil) (nntp "flab.flab.edu"))) + (override-methods '((nntp "override") (nnnil)))) + (custom-set-variables `(gnus-select-method (quote ,(car test-methods))) + `(gnus-secondary-select-methods (quote ,(cdr test-methods))) + `(gnus-select-methods (quote ,override-methods))) + (should (cl-every #'identity + (cl-mapcar #'gnus-methods-equal-p gnus-select-methods override-methods))) + (should (gnus-method-equal gnus-select-method (car gnus-select-methods))) + (should (cl-every #'identity + (cl-mapcar #'gnus-methods-equal-p gnus-secondary-select-methods + (cdr gnus-select-methods)))))) + +(ert-deftest gnus-test-gnus-start-news-server () + "Test an archaic method of initiating gnus." + (let (gnus-current-select-method + (gnus-nntp-server "::")) + (cl-letf (((symbol-function 'gnus-y-or-n-p) #'ignore)) + (gnus-start-news-server) + (should (gnus-method-equal gnus-select-method `(nnspool ,(system-name))))))) + +(ert-deftest gnus-test-gnus-read-active-file () + "Ensure unification does right by `gnus-read-active-file'." + (let (gnus-select-method + gnus-secondary-select-methods + gnus-select-methods + (test-methods '((nnnil) (nntp "flab.flab.edu")))) + (custom-set-variables `(gnus-select-methods (quote ,test-methods))) + (should (equal + (cl-remove-if (lambda (method) + (gnus-method-equal method gnus-select-method)) + gnus-select-methods) + gnus-secondary-select-methods)))) + +;;; gnus-test-select-methods.el ends here diff --git a/test/lisp/gnus/gnus-tests.el b/test/lisp/gnus/gnus-tests.el index 6602e67a34..c65c2ce880 100644 --- a/test/lisp/gnus/gnus-tests.el +++ b/test/lisp/gnus/gnus-tests.el @@ -25,9 +25,95 @@ ;; tests that are not standalone. ;;; Code: -;; registry.el is required by gnus-registry.el but this way we're explicit. -(require 'registry) -(require 'gnus-registry) +;; + +(require 'cl-macs) +(require 'message) +(require 'gnus) +(require 'gnus-start) +(require 'nsm) + +(defconst gnus-tests-load-file-name (or load-file-name + (buffer-file-name))) + +(defmacro gnus-tests-let-customs (bindings &rest forms) + (declare (indent defun)) + `(let (,@(mapcar #'car bindings)) + (ignore ,@(mapcar #'car bindings)) + (funcall #'custom-set-variables + ,@(mapcar (apply-partially #'list 'quote) bindings)) + ,@forms)) + +(cl-defmacro gnus-tests-doit (&rest + body + &key + (select-methods '(default-value 'gnus-select-methods)) + (customs) + &allow-other-keys + &aux + (body + (cl-loop until (not (keywordp (car body))) + do (setq body (nthcdr 2 body)) + finally return body))) + (declare (indent defun)) + `(let* ((parent-dir (file-name-directory gnus-tests-load-file-name)) + (default-directory (file-name-as-directory (concat parent-dir "gnus-tests"))) + (user-emacs-directory default-directory)) + (unless (file-exists-p default-directory) + (make-directory default-directory)) + (gnus-tests-let-customs + ((gnus-verbose 8) + (gnus-save-dot-newsrc nil) + (gnus-home-directory default-directory) + (gnus-use-dribble-file nil) + (network-security-level (quote low)) + (gnus-interactive-exit (quote quiet)) + (gnus-select-methods ,select-methods) + (message-directory (concat default-directory "Mail")) + (mail-source-directory message-directory) + (mail-source-crash-box (concat default-directory ".whatev")) + (gnus-newsrc-file (nnheader-concat gnus-home-directory ".newsrc.eld")) + (gnus-init-file (nnheader-concat gnus-home-directory ".gnus")) + (gnus-directory (nnheader-concat gnus-home-directory "News/")) + ,@customs) + (unwind-protect + (progn ,@body) + (cl-macrolet ((safe-delete + (x) + `(if (cl-search "gnus-tests/" ,x) + (delete-directory ,x t) + (error "Attempted delete of %s!" ,x)))) + (safe-delete default-directory)))))) + +(ert-deftest gnus-test-clean-room () + (gnus-tests-doit + (should (equal gnus-select-methods (default-value 'gnus-select-methods))) + (should-not gnus-save-dot-newsrc)) + (should gnus-save-dot-newsrc)) + +(ert-deftest gnus-test-select-methods () + (gnus-tests-doit :select-methods (quote ((nnfolder ""))) + (should (equal gnus-select-methods (quote ((nnfolder ""))))) + (should (equal gnus-select-method (quote (nnfolder "")))) + (should-not gnus-secondary-select-methods))) + +(ert-deftest gnus-test-be-nicer-to-noobs () + "Between the time Linux entered the home and the time I wrote this test, +the innocent user trying `M-x gnus` would be rebuffed with hostility." + (gnus-tests-doit + (with-current-buffer "*Messages*" + (let ((inhibit-read-only t)) + (erase-buffer))) + (let ((inhibit-message t)) + (call-interactively #'gnus)) + (with-current-buffer "*Messages*" + (save-excursion + (goto-char (point-min)) + (should-error (re-search-forward "failed")))))) + +(ert-deftest gnus-test-basic-op () + (gnus-tests-doit :select-methods (quote ((nnfolder ""))) + (call-interactively #'gnus))) (provide 'gnus-tests) ;;; gnus-tests.el ends here diff --git a/test/src/process-tests.el b/test/src/process-tests.el index b64c82c87d..708473bf8a 100644 --- a/test/src/process-tests.el +++ b/test/src/process-tests.el @@ -111,6 +111,15 @@ process-test-stderr-buffer (goto-char (point-min)) (looking-at "hello stderr!")))))) +(ert-deftest process-test-stopped-pipe () + (skip-unless (executable-find "cat")) + (with-temp-buffer + (let ((proc (make-pipe-process :name "pipe" :buffer (current-buffer) + :command '("cat") :stop t))) + (unwind-protect + (should (list-processes--refresh)) + (delete-process proc))))) + (ert-deftest process-test-stderr-filter () (skip-unless (executable-find "bash")) (with-timeout (60 (ert-fail "Test timed out")) diff --git a/test/src/thread-tests.el b/test/src/thread-tests.el index fc7bc7441b..2b0fcb5383 100644 --- a/test/src/thread-tests.el +++ b/test/src/thread-tests.el @@ -20,6 +20,8 @@ ;;; Code: (require 'thread) +(require 'eieio) +(require 'ring) ;; Declare the functions in case Emacs has been configured --without-threads. (declare-function all-threads "thread.c" ()) @@ -42,6 +44,26 @@ (declare-function thread-yield "thread.c" ()) (defvar main-thread) +(defclass threads-test-channel () + ((condition :initarg :condition :type condition-variable) + (msg-queue :initarg :msg-queue :type ring))) + +(cl-defgeneric threads-test-channel-send ((channel threads-test-channel) message) + (with-slots (condition msg-queue) channel + (with-mutex (condition-mutex condition) + (while (<= (ring-size msg-queue) (ring-length msg-queue)) + (condition-wait condition)) + (ring-insert msg-queue message) + (condition-notify condition t)))) + +(cl-defgeneric threads-test-channel-recv ((channel threads-test-channel)) + (with-slots (condition msg-queue) channel + (with-mutex (condition-mutex condition) + (while (ring-empty-p msg-queue) + (condition-wait condition)) + (prog1 (ring-remove msg-queue) + (condition-notify condition t))))) + (ert-deftest threads-is-one () "Test for existence of a thread." (skip-unless (featurep 'threads)) @@ -318,7 +340,7 @@ threads-signal-early (make-thread (lambda () (while t (thread-yield)))))) (thread-signal thread 'error nil) - (sit-for 1) + (funcall (if noninteractive #'sit-for #'sleep-for) 1) (should-not (thread-live-p thread)) (should (equal (thread-last-error) '(error))))) @@ -389,7 +411,99 @@ threads-condvar-wait (should (equal (thread-last-error) '(error "Die, die, die!"))))) (ert-deftest threads-test-bug33073 () + (skip-unless (featurep 'threads)) (let ((th (make-thread 'ignore))) (should-not (equal th main-thread)))) -;;; threads.el ends here +(ert-deftest threads-test-bug36609-signal () + "Would only fail under TEST_INTERACTIVE=yes, and not every time. +The failure manifests only by being unable to exit the interactive emacs." + (skip-unless (featurep 'threads)) + (let* ((cv (make-condition-variable (make-mutex) "CV")) + condition + (notify (lambda () + (sleep-for 1) ;; let wait() start spinning first + (with-mutex (condition-mutex cv) + (setq condition t) + (condition-notify cv)))) + (wait (lambda () (with-mutex (condition-mutex cv) + (while (not condition) + (condition-wait cv))))) + (herring (make-thread (apply-partially #'sleep-for 1000) "unrelated"))) + ;; herring is a non-main thread that, if the bug is still present, + ;; could assume the glib context lock when the main thread executes wait() + (make-thread notify "notify") + (funcall wait) + (thread-signal herring 'quit nil))) + +(ert-deftest threads-test-glib-lock () + "Would only fail under TEST_INTERACTIVE=yes, and not every time. +The failure manifests only by being unable to exit the interactive emacs." + (skip-unless (featurep 'threads)) + (cl-macrolet ((run-thread + (name what) + `(make-thread + (lambda () + (sleep-for (1+ (random 3))) + (funcall ,what)) + ,name))) + (let* ((n 3) + (capacity 1) + (channel (make-instance + 'threads-test-channel + :condition (make-condition-variable (make-mutex) "channel") + :msg-queue (make-ring capacity)))) + (dotimes (i n) + (let ((send-name (format "send-%d" (1+ i))) + (recv-name (format "recv-%d" (- n i)))) + (run-thread send-name + (lambda () (threads-test-channel-send channel 42))) + (run-thread recv-name + (lambda () (threads-test-channel-recv channel)))))))) + +(ert-deftest threads-test-promiscuous-process () + "Can't we `accept-process-output' of a process started by another thread? +For whatever reason, in 2012, tromey inserted an assertion forbidding this. +We test flouting that edict here." + (skip-unless (featurep 'threads)) + (thread-last-error t) + (let* ((thread-tests-main (get-buffer-create "thread-tests-main" t)) + (buffers (list thread-tests-main)) + (start-proc (lambda (n b) + (apply #'start-process n b "cat" (split-string "/dev/urandom")))) + (n 3)) + (funcall start-proc "threads-tests-main" (car buffers)) + (dotimes (i (1- n)) + (push (get-buffer-create (format "thread-tests-%d" i) t) buffers) + (make-thread (apply-partially start-proc + (format "thread-tests-%d" i) + (car buffers)))) + (should (cl-loop repeat 10 + when (cl-every #'processp (mapcar #'get-buffer-process buffers)) + return t + do (accept-process-output nil 0.1) + finally return nil)) + (let ((procs (mapcar #'get-buffer-process buffers))) + (mapc (lambda (proc) (set-process-thread proc nil)) procs) + (dotimes (i (1- n)) + (make-thread + (lambda () + (cl-loop repeat 5 + do (accept-process-output + (nth (random (length procs)) procs) + 0.2 + nil + t))) + (format "thread-tests-%d" i))) + (should (cl-loop repeat 20 + unless (cl-some + (lambda (thr) + (cl-search "thread-tests-" (thread-name thr))) + (all-threads)) + return t + do (accept-process-output + (nth (random (length procs)) procs) 1.0) + finally return nil))) + (mapc (lambda (b) (kill-buffer b)) buffers)) + (should-not (thread-last-error t))) +;;; thread-tests.el ends here -- 2.26.2