Subject: 29.0.50; Use warnings facility for reporting Gnus errors

From: Eric Abrahamsen <[email-masked]>
Date: Fri, 22 Oct 2021 10:04:39 -0700

In gnus.user there was a longish conversation about how to better report
the failure of Gnus mail source fetching to the user.

I originally went off on a grand adventure of defining custom errors for
various kinds of Gnus situations, for use in flow control. I still think
that's a good idea, but `nnheader-report' currently does the core of
that job for backend-specific errors, and it's fairly well-developed,
and there's not necessarily anything that needs fixing there.

And the original bug report was more about making errors visible to the
user than flow control, so I went back to that. Apart from fundamental
backend errors, other errors and failures are surfaced with
`gnus-message' and `gnus-error'. Both are gated by the integer value of
`gnus-verbose': higher numbers indicate less-important messages.

The more I fooled with things, the more it looked like improvements
could be made in `gnus-error'. It does a few things:

- Calls `ding'. This function returns nil on my system, dunno if it does
  anything on other systems.
- Displays the error using `message'.
- If the error level is a float, it uses the "float part" as a number of
  seconds to `sit-for' while displaying the error message. So an error
  level 4.5 would (if `gnus-verbose' were 4 or lower) display for 5
  seconds.

So obviously the purpose of this function is to get the user's
attention, in appropriate situations. But `ding' doesn't seem to do
anything, and there are only three places in the Gnus codebase where
`gnus-error' is called with a float value. That means there are only
three places where the `message' isn't immediately swallowed by whatever
comes next (and there's almost always another message coming next),
which really means there are only three places where `gnus-error' does
anything different from `gnus-message'.

The point here was to alert the user to failures in a non-annoying way,
and I think the warnings facility might be a better way of doing that.
`delay-warning', in particular, with a custom warning buffer. The
attached is a code sketch of that. Some points:

- It's hard-coded to prevent buffer pop-up, instead letting the user add
  the buffer to Gnus' window configuration, or call an interactive
  command to see it. We could also do something like put a note in the
  mode-line when there are new log messages to view.
- I've referred to all this as "logging" rather than "warnings", as that
  seems more general.
- gnus-message can also add strings to `gnus-action-message-log', which
  is consulted at the end of startup and maybe displayed with
  `gnus-final-warning'. That only happens once, at startup; it seems
  like a complicated mechanism to only use once. I think this could be
  replaced by warnings.
- Actually I think both `gnus-message' and `gnus-error' could be
  replaced with a `gnus-log' function, but that's something that could
  be played with later on.

I think the main concerns here are making sure the user actually sees
important messages, through a combination of splitting them off into
their own buffer, so they don't get lost in *Messages*, and potentially
delaying display until a particular action is complete, and the user has
a chance to see them.

diff --git a/lisp/gnus/gnus-util.el b/lisp/gnus/gnus-util.el
index a777157f89..58edc55877 100644
--- a/lisp/gnus/gnus-util.el
+++ b/lisp/gnus/gnus-util.el
@@ -511,15 +511,10 @@ gnus-final-warning
 	     (mapconcat #'identity gnus-action-message-log "; "))))
 
 (defun gnus-error (level &rest args)
-  "Beep an error if LEVEL is equal to or less than `gnus-verbose'.
+  "Log an error if LEVEL is equal to or less than `gnus-verbose'.
 ARGS are passed to `message'."
   (when (<= (floor level) gnus-verbose)
-    (apply #'message args)
-    (ding)
-    (let (duration)
-      (when (and (floatp level)
-		 (not (zerop (setq duration (* 10 (- level (floor level)))))))
-	(sit-for duration))))
+    (delay-warning '(gnus) (apply #'message args) :warning "*Gnus Log*"))
   nil)
 
 (defun gnus-split-references (references)
@@ -1252,6 +1247,16 @@ gnus-create-info-command
     (setq gnus-info-buffer (current-buffer))
     (gnus-configure-windows 'info)))
 
+(defun gnus-display-log ()
+  "Pop up a window displaying the *Gnus Log* buffer."
+  (interactive)
+  ;; We just use plain `display-buffer' here.  If the user wants to
+  ;; compose the log buffer as part of a Gnus window configuration,
+  ;; they can do that in `gnus-buffer-configuration'.  If they want to
+  ;; control how this display works, they can configure
+  ;; `display-buffer-alist'.
+  (display-buffer "*Gnus Log*"))
+
 (defun gnus-not-ignore (&rest _args)
   t)
 
diff --git a/lisp/gnus/gnus-win.el b/lisp/gnus/gnus-win.el
index 8ac4e39fa5..9fafe88bcf 100644
--- a/lisp/gnus/gnus-win.el
+++ b/lisp/gnus/gnus-win.el
@@ -188,6 +188,7 @@ gnus-window-to-buffer
     (score-trace . "*Score Trace*")
     (split-trace . "*Split Trace*")
     (info . gnus-info-buffer)
+    (log . "*Gnus Log*")
     (category . gnus-category-buffer)
     (article-copy . gnus-article-copy)
     (draft . gnus-draft-buffer)
diff --git a/lisp/gnus/gnus.el b/lisp/gnus/gnus.el
index 6644cc4d81..c162b58d22 100644
--- a/lisp/gnus/gnus.el
+++ b/lisp/gnus/gnus.el
@@ -48,6 +48,11 @@ gnus-spam-resend-to
 (defvar gnus-ham-resend-to)
 (defvar gnus-spam-process-newsgroups)
 
+;; We suppress the display of all Gnus warnings: they go to a separate
+;; buffer, and should only be displayed as part of Gnus' own window
+;; display routines (or explicitly with `gnus-display-log').
+
+(cl-pushnew 'gnus warning-suppress-types)
 
 (defgroup gnus nil
   "The coffee-brewing, all singing, all dancing, kitchen sink newsreader."
diff --git a/lisp/gnus/mail-source.el b/lisp/gnus/mail-source.el
index af0a198376..c2ec48cc86 100644
--- a/lisp/gnus/mail-source.el
+++ b/lisp/gnus/mail-source.el
@@ -554,18 +554,15 @@ mail-source-fetch
 		 (condition-case err
 		     (funcall function source callback)
 		   (error
-		    (if (and (not mail-source-ignore-errors)
-			     (not
-			      (yes-or-no-p
-			       (format "Mail source %s error (%s).  Continue? "
-				       (if (memq ':password source)
-					   (let ((s (copy-sequence source)))
-					     (setcar (cdr (memq ':password s))
-						     "********")
-					     s)
-					 source)
-				       (cadr err)))))
-		      (error "Cannot get new mail"))
+		    (unless mail-source-ignore-errors
+		      (error "Mail source %s error (%s)"
+                             (if (memq ':password source)
+                                 (let ((s (copy-sequence source)))
+                                   (setcar (cdr (memq ':password s))
+	                                   "********")
+                                   s)
+                               source)
+                             (cadr err)))
 		    0)))))))))
 
 (declare-function gnus-message "gnus-util" (level &rest args))
diff --git a/lisp/gnus/nnmail.el b/lisp/gnus/nnmail.el
index bcf01cfa9e..a9a778f458 100644
--- a/lisp/gnus/nnmail.el
+++ b/lisp/gnus/nnmail.el
@@ -1842,7 +1842,7 @@ nnmail-get-new-mail-1
 							    src)))
 			      ansym))))
 		      ((error quit)
-		       (message "Mail source %s failed: %s" source cond)
+		       (gnus-error 5 cond)
 		       0)))
 	  (cl-incf total new)
 	  (cl-incf i)))