Subject: RE: [External] : Re: bug#78658: 30.1; [PATCH] Dired feature suggestion: dired-on-marked-files-in-all-buffers

From: Drew Adams <[email-masked]>
Date: Sun, 8 Jun 2025 23:42:21 +0000

> > > If we allow find-dired to add its buffers to the list in
> > > dired-buffers, we need to make sure they are removed from the list.
> > > We also need to make sure that functions which scan the list and
> > > operate on its buffers will DTRT with find-dired buffers found in the
> > > list.  Is all of this reasonable to do, and if so, is it planned to be
> > > done as part of the changes you are discussing?
> >
> > I'm afraid I don't understand.
> >
> > 1. What do you mean by making sure they're removed from the list?  The only
> reason to remove a file/dir from the list is, as is the case with WDired, if
> the mode is changed from `dired-mode' to something else. (WDired removes its
> buffer from the list, for that reason.)
> >
> > As I said, there are no uses of `dired-buffers' in the Emacs source code,
> apart from what I mentioned - no function that accesses `dired-buffers' at
> all.
> >
> > 2. What do you mean by a function operating on Dired buffers doing the
> right thing? What can that possibly mean, independent from any particular
> such function?
> >
> > Again, no Emacs source code accesses (in order to operate) on its buffers.
> 
> It is accessed in dired.el, AFAICT.

Apart from `dired-(un)advertise', which I addressed, it seems
to be used only by `dired-find-buffer-nocreate' and others to
access one of the `dired-buffers', which is normal.

And I don't see any harm from the functions in `dired.el'
using Dired buffers created by find*, and I don't think any
is possible.  (Famous last words, perhaps?)

> What I meant is that if find-dired wants to use this variable, it
> needs to play by the rules the other users do.

I don't see any rules" that the others (which means dired.el
functions) play by.  `dired-advertise' adds to the list and
`dired-unadvertise' subtracts from it.  The other functions
that access it just use a buffer from `dired-buffers' for
Dired, which is normal.

> Don't ask me about the
> details, because I'm not familiar with them.

I won't ask - I think I've said what they are (above).
But if I'm missing something, please let me know what.

> But it is clear that
> there's a protocol wrt this variable, which determines when and which
> buffers are added to the list and when they are removed.  I'm saying
> that find-dired should abide by the same protocol.

Adding and removing is done only by `dired-(un)advertise'.
I've spoken to that.  `dired' invokes advertise when it
sets the mode to `dired-mode'.  The only functions that
call `dired-unadvertise' are `dired-rename-subdir' and
`wdired-change-to-wdired-mode', which I think I've
addressed.

find* has only to remove the binding of `dired-buffers',
to cause it, like `dired', to add an entry.  And find* has
no reason to ever remove a `dired-buffers' entry.  If one
needs to be removed because of deletion of a top-level
directory then `dired-rename-subdir' will do that.  And if
one needs to be removed because of entering WDired mode
then `wdired-change-to-wdired-mode' will do that.

Again, I don't think this is any different than the case of
a Dired buffer with an arbitrary listing (cons DIRNAME arg).

> As for the DTRT part, what I meant was to ask whether the current
> users of dired-buffers will know what to do with buffers put there by
> find-dired, or might they be surprised and confused by them?

I don't see how or why they would be surprised, or even
recognize such a buffer as coming from find*.  And they
shouldn't to do anything special or different for such a
buffer.

Again, I think this is the same as the case of a Dired
buffer with an arbitrary listing.

> Buffers
> created by find-dired are in Dired mode, but they are not exactly
> Dired buffers, in the sense that they don't necessarily show files
> that live in the same directory.

That's exactly the case of a Dired buffer with a set of
arbitrary files, i.e., what happens if you invoke `dired'
with a DIRNAME arg that's a cons (of a buffer name and a
list of files from anywhere - even on different file-system
trees, e.g. different Windows drives).

There's no special handling of Dired buffers with arbitrary
files - their buffers are happily included in `dired-buffers',
with no problem.  That's been the case since Day One, AFAIK.

> E.g., what will happen if the user invokes Wdired on such a buffer?

I think it should work as well as it does now.  After you,
e.g., rename some files and dirs in the buffer, and you use
`C-x C-q' again, those files and dirs should stay renamed.
That's what happens if you do that in a Dired buffer with
arbitrary files.

> Again, please don't ask me to provide you with detailed instructions,
> because I don't have them.  I asked the questions that bothered me wrt
> to allowing find-dired to use this variable, but the answers should
> come from people who want to allow that, after they do the necessary
> research into the current uses of the variable and the possible
> effects of allowing find-dired add its buffers to the list.
> 
> Put it in other words: currently dired-buffers seems to be an internal
> variable used by Dired for its own purposes; if find-dired wants to
> become its user, it should make sure it will play by the same rules so
> as not to disrupt its current users.

I hear you, but I don't know what else to say.  I do
agree that someone should give it a try and see if they
encounter problems that aren't there already.  I'm not
too well placed to do that.

I did try, with vanilla Emacs 30.1, to first see what
happens if you just try (with no changes) to use WDired
on a find* Dired listing.

Then I tried doing the same with the only change being
the removal of binding `(dired-buffers dired-buffers)'
from `find-dired-with-command'.  (It's actually in that
function that the binding is, in 30.1; it used to be in
function `find-dired' itself.)

In both cases I ran into this error with Emacs 30.1
when I tried to exit WDired mode after renaming a file:

Debugger entered--Lisp error: (void-variable wdired--old-marks)
  (dired-mark-remembered wdired--old-marks)
  (let ((inhibit-read-only t)) (dired-mark-remembered wdired--old-marks))
  (progn (cond ((and (stringp dired-directory) (not (file-directory-p dired-directory)) (null some-file-names-unchanged) (= (length files-renamed) 1)) (setq dired-directory (cdr (car files-renamed)))) ((and (consp dired-directory) (cdr dired-directory) files-renamed) (setq dired-directory (cons (car dired-directory) (let* ((--cl-var-- ...) (f nil) (--cl-var-- nil)) (while (consp --cl-var--) (setq f ...) (setq --cl-var-- ...) (setq --cl-var-- ...)) (nreverse --cl-var--)))))) (revert-buffer) (let ((inhibit-read-only t)) (dired-mark-remembered wdired--old-marks)))
  (if changes (progn (cond ((and (stringp dired-directory) (not (file-directory-p dired-directory)) (null some-file-names-unchanged) (= (length files-renamed) 1)) (setq dired-directory (cdr (car files-renamed)))) ((and (consp dired-directory) (cdr dired-directory) files-renamed) (setq dired-directory (cons (car dired-directory) (let* (... ... ...) (while ... ... ... ...) (nreverse --cl-var--)))))) (revert-buffer) (let ((inhibit-read-only t)) (dired-mark-remembered wdired--old-marks))) (let ((inhibit-read-only t)) (remove-text-properties (point-min) (point-max) '(old-name nil end-name nil old-link nil end-link nil end-perm nil old-perm nil perm-changed nil)) (message "(No changes to be performed)") (revert-buffer)))
  (let ((changes nil) (errors 0) files-deleted files-renamed some-file-names-unchanged file-old file-new tmp-value) (save-excursion (if (and wdired-allow-to-redirect-links (fboundp 'make-symbolic-link)) (progn (setq tmp-value (wdired-do-symlink-changes)) (setq errors (cdr tmp-value)) (setq changes (car tmp-value)))) (if (and wdired-allow-to-change-permissions wdired--perm-beg) (progn (setq tmp-value (wdired-do-perm-changes)) (setq errors (+ errors (cdr tmp-value))) (setq changes (or changes (car tmp-value))))) (goto-char (point-max)) (while (not (bobp)) (setq file-old (and (wdired--line-preprocessed-p) (wdired-get-filename nil t))) (if file-old (progn (setq file-new (wdired-get-filename)) (if (equal file-new file-old) (setq some-file-names-unchanged t) (setq changes t) (if (not file-new) (setq files-deleted ...) (if wdired-keep-marker-rename ...) (setq files-renamed ...))))) (forward-line -1))) (if files-renamed (progn (let* ((val (wdired-do-renames files-renamed))) (progn (ignore (consp val)) (let* ((x0 ...) (x1 ...)) (let (... ...) (progn ... ...))))))) (wdired-change-to-dired-mode) (if changes (progn (cond ((and (stringp dired-directory) (not (file-directory-p dired-directory)) (null some-file-names-unchanged) (= (length files-renamed) 1)) (setq dired-directory (cdr (car files-renamed)))) ((and (consp dired-directory) (cdr dired-directory) files-renamed) (setq dired-directory (cons (car dired-directory) (let* ... ... ...))))) (revert-buffer) (let ((inhibit-read-only t)) (dired-mark-remembered wdired--old-marks))) (let ((inhibit-read-only t)) (remove-text-properties (point-min) (point-max) '(old-name nil end-name nil old-link nil end-link nil end-perm nil old-perm nil perm-changed nil)) (message "(No changes to be performed)") (revert-buffer))) (if files-deleted (progn (wdired-flag-for-deletion files-deleted))) (if (> errors 0) (progn (dired-log-summary (format "%d actions failed" errors) nil))))
  wdired-finish-edit()
  (if (y-or-n-p (format "Buffer %s modified; save changes? " (current-buffer))) (wdired-finish-edit) (wdired-abort-changes))
  (if (buffer-modified-p) (if (y-or-n-p (format "Buffer %s modified; save changes? " (current-buffer))) (wdired-finish-edit) (wdired-abort-changes)) (wdired-change-to-dired-mode) (set-buffer-modified-p nil) (setq buffer-undo-list nil) (message "(No changes need to be saved)"))
  wdired-exit()
  funcall-interactively(wdired-exit)
  command-execute(wdired-exit)

I tried to do this on Windows (11), with Cygwin.  Dunno if
someone else will see the same thing.

I also tried with vanilla Emacs 26.3.  There I didn't get any
error, and I saw the same behavior wrt WDired with and without
the (dired-buffers dired-buffers) binding in `find-dired'
(that's the function that has the binding, in Emacs 26.3).

But in Emacs 26 when exiting WDired mode (with or without
the dired-buffers binding) the find Dired buffer is reverted
to the directory where the find was issued (same dir as the
find result), but showing the full directory, not just the
found files.  What's more, in that Dired listing, preceding
the privileges (-rw-rw-rw-) there are two additional columns,
with values such as these:

      281474977338773   0
      281474977338774   1
      281474977338792   1
      281474977338758   1
      ...

So in 26.3, at least, there's a problem with exiting
WDired mode, but it's unrelated to the proposed change.

In sum, I don't seem to be able to test this well.
But from what I do see, there's no difference, wrt
using WDired with find*, with or without the binding
(dired-buffers dired-buffers).  And in both cases the
renamed files stay renamed, wherever in the find*
results they may be (as one would expect).

I'm hoping someone else will try the same thing or
similar, on Windows or another platform, to see if
removing the dired-buffers binding from find* has any
negative consequence.  Even a quick sanity test would
be good.  I'm afraid I'm no more help, here.

> > I suspect that the prohibition for `find*' was overengineering, for lack of
> full understanding of Dired.
> 
> I rather guess that there was a real problem which caused that, but
> that problem is lost to time, or at least I wasn't able to find its
> description.  The date of the change predates all the mailing lists we
> nowadays use, including even my private archives of the private
> mailing list we used back then.

Understood.

My suggestion is to make the proposed change, but to
also provide a defvar to choose to allow/disallow
inclusion of find* Dired buffers in `dired-buffers',
i.e., choose whether to bind `dired-buffers'.

I'd also suggest that by default the defvar should
have find* buffers be included, not excluded.  If ever
a problem from that is reported we could change the
default value, but I don't expect such a problem.

Maybe I'm too optimistic.  Still, it would be good to
be able to choose, at least.