Subject: bug#78666: 31.0.50; recentf-open-files reports opening the last file accessed

From: Stephen Berman <[email-masked]>
Date: Wed, 04 Jun 2025 15:37:11 +0200

On Tue, 3 Jun 2025 19:21:19 -0500 Rick <[email-masked]> wrote:

[I moved the quoted message from me up for continuity.]
> On 6/3/25 17:51, Stephen Berman wrote:
>> On Sun, 1 Jun 2025 18:34:45 -0500 Rick<[email-masked]> wrote:
>>
>>> --text follows this line--
>>> You can recreate most of the problem starting with -Q however you must
>>> have a previously populated recentf file before starting.
>>>
>>> M-x recentf-mode
>>> M-x recentf-open-files
>>>
>>> Now switch to the *Messagers* buffer and notice something similar to:
>>>
>>>      Loading /home/rick/.emacs.d/recentf...done     Cleaning up the recentf
>>> list...done (0 removed)     Mark set     Open
>>> /snap/emacs/2827/usr/share/emacs/31.0.50/lisp/files.el.gz
>>>
>>> Observe that it claims to have opened the most recent item on recentf-list,
>>> as loaded from the pre-populated recentf file.  In my case .../files.el.gz.
>>>
>>> There doesn't seem to be a buffer actually associated with the file but
>>> it's unclear whether it was subsequently closed or a spurious message.
>> I think you misunderstood those messages: each one is help text to
>> inform you that by checking the box of that item you will open the file
>> (in GUI Emacs you can see the same text in a tooltip if you move the
>> mouse pointer over the item).  So they are not informing you that the
>> file has been opened.
>>
>> Steve Berman
>
> It seems you are confirming that the messages are in fact spurious
> at this point in the execution process because they are generated
> prior to the window being presented to the user.   You are
> describing messages that are only useful once the user starts
> being able to interact with the buffer so other than at this point
> they are correct, necessary, appropriate.
>
> Given I'm not mistaken, I'm now much more confident that the
> fix I suggested an hour or so ago ago is only pretty close to
> the correct way to deal with it.  I will now amend my suggestion
> to the following.
>
> Add the optional parameter (accepted by widget-move) to
> recentf-dialog-goto-first so that 't' can be passed to widget-move
> when recentf-edit-list and recentf-open-files call it.  All other
> callers will default to nil and therefore be unaffected.  This should
> cleanly remove them exactly and only where they are inappropriate.
>
> (defun recentf-dialog-goto-first (widget-type *_&optional quietly_*)
>   "Move the cursor to the first WIDGET-TYPE in current dialog.
> Go to the beginning of buffer if not found."
>   (goto-char (point-min))
>   (condition-case nil
>       (let (done)
>         (widget-move 1 _*quietly*_)
>         (while (not done)
>           (if (eq widget-type (widget-type (widget-at (point))))
>               (setq done t)
>             (widget-move 1 _*quietly*_))))
>     (error
>      (goto-char (point-min)))))
>
>
> (defun recentf-open-files (&optional files buffer-name)
> ...
>     (recentf-dialog-goto-first 'link *_t_*)))
>
>
> (defun recentf-edit-list ()
> ...
>     (recentf-dialog-goto-first 'checkbox *_t_*)))

I took a closer look at recentf.el and found that the messages (which
come from the :help-echo property in `recentf-open-files-item') are
displayed only on invoking `recentf-open-files' and on tabbing between
items in the *Open Recent* buffer; invoking `recentf-edit-list' or
tabbing between check boxes in that list does not display such a message
(so my assertion above was wrong in referring to check boxes).  So you
don't need to change `recentf-edit-list'.  (And in fact, it isn't
necessary to change `recentf-open-files' either, see the attached
patch.)

Also, your change only affects displaying the message on creating the
open file dialog; further messages are still displayed when tabbing
between items in that buffer.  In your second post you said you saw
several messages, also repeated ones.  AFAICT all messages but the first
can only result from tabbing between items; if this is not what you see,
please give a precise recipe detailing what you find problematic.

If you do also want to suppress the messages on tabbing between items,
then I think more extensive changes are needed.  For one thing, since
displaying the messages has long been the status quo, suppressing them
should be optional (though I agree with you that displaying the message
on creating the *Open Recent* buffer seems like a bug).  Moreover, since
tabbing invokes the commands `widget-forward' and `widget-backward'
(which in turn invoke `widget-move'), these need to be changed as well.
The attached patch implements these changes.

Steve Berman
diff --git a/lisp/recentf.el b/lisp/recentf.el
index a773ea9ec01..71f3eedfe43 100644
--- a/lisp/recentf.el
+++ b/lisp/recentf.el
@@ -330,6 +330,14 @@ recentf-show-messages
   :group 'recentf
   :type 'boolean
   :version "31.1")
+
+(defcustom recentf-suppress-open-file-help nil
+  "If non-nil, suppress help message on tabbing in open file dialog.
+If nil, show the help message on tabbing.  (In any case, the help text
+is displayed in a tooltip when the mouse pointer moves over the item)."
+  :group 'recentf
+  :type 'boolean
+  :version "31.1")
 
 ;;; Utilities
 ;;
@@ -1099,15 +1107,34 @@ recentf-dialog-goto-first
 Go to the beginning of buffer if not found."
   (goto-char (point-min))
   (condition-case nil
-      (let (done)
-        (widget-move 1)
+      (let ((no-echo (or recentf-suppress-open-file-help
+                         (not (or (eq this-command #'recentf-forward)
+                                  (eq this-command #'recentf-backward)))))
+            done)
+        (widget-move 1 no-echo)
         (while (not done)
           (if (eq widget-type (widget-type (widget-at (point))))
               (setq done t)
-            (widget-move 1))))
+            (widget-move 1 no-echo))))
     (error
      (goto-char (point-min)))))
 
+(defun recentf-forward (arg)
+  "Move the cursor to the next widget in the current dialog.
+With prefix argument ARG, move to the ARGth next widget.  If
+`recentf-suppress-open-file-help' is non-nil, suppress help messages in
+the echo area in the open recentf dialog."
+  (interactive "p")
+  (widget-forward arg recentf-suppress-open-file-help))
+
+(defun recentf-backward (arg)
+  "Move the cursor to the previous widget in the current dialog.
+With prefix argument ARG, move to the ARGth previous widget.  If
+`recentf-suppress-open-file-help' is non-nil, suppress help messages in
+the echo area in the open recentf dialog."
+  (interactive "p")
+  (widget-backward arg recentf-suppress-open-file-help))
+
 (defvar-keymap recentf-dialog-mode-map
   :doc "Keymap used in recentf dialogs."
   :parent (make-composed-keymap recentf--shortcuts-keymap widget-keymap)
@@ -1139,6 +1166,8 @@ recentf-dialog
     (recentf-dialog-mode)
     ,@forms
     (widget-setup)
+    (keymap-local-set "<remap> <widget-forward>" #'recentf-forward)
+    (keymap-local-set "<remap> <widget-backward>" #'recentf-backward)
     (switch-to-buffer (current-buffer))))
 
 ;;; Edit list dialog
diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el
index db241ca914a..311e39f4c0f 100644
--- a/lisp/wid-edit.el
+++ b/lisp/wid-edit.el
@@ -1383,19 +1383,23 @@ widget-move
     (widget-echo-help (point)))
   (run-hooks 'widget-move-hook))
 
-(defun widget-forward (arg)
+(defun widget-forward (arg &optional suppress-echo)
   "Move point to the next field or button.
-With optional ARG, move across that many fields."
+With optional ARG, move across that many fields.
+When the second optional argument is non-nil,
+nothing is shown in the echo area."
   (interactive "p")
   (run-hooks 'widget-forward-hook)
-  (widget-move arg))
+  (widget-move arg suppress-echo))
 
-(defun widget-backward (arg)
+(defun widget-backward (arg &optional suppress-echo)
   "Move point to the previous field or button.
-With optional ARG, move across that many fields."
+With optional ARG, move across that many fields.
+When the second optional argument is non-nil,
+nothing is shown in the echo area."
   (interactive "p")
   (run-hooks 'widget-backward-hook)
-  (widget-move (- arg)))
+  (widget-move (- arg) suppress-echo))
 
 ;; Since the widget code uses a `field' property to identify fields,
 ;; ordinary beginning-of-line does the right thing.