Subject: dired-allow-duplicate-buffers

From: Boruch Baum <[email-masked]>
Date: Thu, 15 Oct 2020 15:22:31 -0400

The attached patch adds a defcustom option to allow multiple buffers to
visit the same directory. This became desirable to me for an extension I
wrote (soon to be submitted) to store each chain of dired buffers'
'navigation history' and allow going back/forward to any point in the
history. As you all know, this has been a feature common to all file
*managers* since almost forever; technically dired is a directory
editor, not a file manager, but I expect many will find the feature
desirable, even expected, since all other similar programs have it.

In going through the existing dired code, it seems that some functions
presume that multiple buffers can be visiting the same directory. See:

  dired-find-buffer-nocreate
  dired-fun-in-all-buffers
  dired-clean-up-after-deletion

Also, implicit in the comment for `dired-find-buffer-nocreate' is that
more than one match may exist for a dired directory buffer.

So, maybe this once existed or was planned?

The first patch is for the option. The second patch is a change of
comments to docstrings.

--
hkp://keys.gnupg.net
CA45 09B5 5351 7C11 A9D1  7286 0036 9E45 1595 8BC0
diff --git a/NEWS b/NEWS
index a76447d..ca7f31b 100644
--- a/NEWS
+++ b/NEWS
@@ -284,6 +284,10 @@ time zones will use a form like "+0100" instead of "CET".

 ** Dired

++++
+*** New user option: 'dired-allow-duplicate-buffers'
+If set, allows multiple dired buffers to visit the same directory.
+
 ---
 *** Broken and circular links are shown with the 'dired-broken-symlink' face.

diff --git a/dired.el b/dired.el
index 7839ace..36c2f17 100644
--- a/dired.el
+++ b/dired.el
@@ -353,6 +353,12 @@ is anywhere on its Dired line, except the beginning of the line."
   :group 'dired
   :version "28.1")

+(defcustom dired-allow-duplicate-buffers t
+  "Allow multiple `dired' buffers to visit the same directory."
+  :type 'boolean
+  :group 'dired
+  :version "28.1")
+
 ;; Internal variables

 (defvar dired-marker-char ?*		; the answer is 42
@@ -1152,9 +1158,11 @@ The return value is the target column for the file names."
   ;; see there.
   (let* ((old-buf (current-buffer))
 	 (dirname (if (consp dir-or-list) (car dir-or-list) dir-or-list))
-         ;; Look for an existing buffer.
-         (buffer (dired-find-buffer-nocreate dirname mode))
-	 ;; Note that buffer already is in dired-mode, if found.
+         (buffer (when (not (bound-and-true-p dired-allow-duplicate-buffers))
+                   ;; Look for an existing buffer.
+                   (dired-find-buffer-nocreate dirname mode)
+                   ;; Note that buffer already is in dired-mode, if found.
+                   ))
 	 (new-buffer-p (null buffer)))
     (or buffer
         (setq buffer (create-file-buffer (directory-file-name dirname))))
diff --git a/dired.el b/dired.el
index 36c2f17..2ecd6bd 100644
--- a/dired.el
+++ b/dired.el
@@ -1143,19 +1143,21 @@ The return value is the target column for the file names."
             (forward-line)))))))

 (defun dired-internal-noselect (dir-or-list &optional switches mode)
-  ;; If DIR-OR-LIST is a string and there is an existing dired buffer
-  ;; for it, just leave buffer as it is (don't even call dired-revert).
-  ;; This saves time especially for deep trees or with ange-ftp.
-  ;; The user can type `g' easily, and it is more consistent with find-file.
-  ;; But if SWITCHES are given they are probably different from the
-  ;; buffer's old value, so call dired-sort-other, which does
-  ;; revert the buffer.
-  ;; Revert the buffer if DIR-OR-LIST is a cons or `dired-directory'
-  ;; is a cons and DIR-OR-LIST is a string.
-  ;; A pity we can't possibly do "Directory has changed - refresh? "
-  ;; like find-file does.
-  ;; Optional argument MODE is passed to dired-find-buffer-nocreate,
-  ;; see there.
+  "Internal function for `dired-noselect'.
+
+If DIR-OR-LIST is a string and there is an existing dired buffer
+for it, just leave buffer as it is (don't even call `dired-revert').
+This saves time especially for deep trees or with `ange-ftp'.
+The user can type `g' easily, and it is more consistent with `find-file'.
+But if SWITCHES are given they are probably different from the
+buffer's old value, so call `dired-sort-other', which does
+revert the buffer.
+Revert the buffer if DIR-OR-LIST is a cons or `dired-directory'
+is a cons and DIR-OR-LIST is a string.
+A pity we can't possibly do \"Directory has changed - refresh?\"
+like `find-file' does.
+Optional argument MODE is passed to `dired-find-buffer-nocreate',
+see there."
   (let* ((old-buf (current-buffer))
 	 (dirname (if (consp dir-or-list) (car dir-or-list) dir-or-list))
          (buffer (when (not (bound-and-true-p dired-allow-duplicate-buffers))
@@ -1219,20 +1221,24 @@ The return value is the target column for the file names."
     buffer))

 (defvar dired-buffers nil
-  ;; Enlarged by dired-advertise
-  ;; Queried by function dired-buffers-for-dir. When this detects a
-  ;; killed buffer, it is removed from this list.
-  "Alist of expanded directories and their associated Dired buffers.")
+  "Alist of expanded directories and their associated Dired buffers.
+The list is enlarged by `dired-advertise'. Function
+`dired-buffers-for-dir' queries this list and removes killed
+buffers from it, when detected.")

 (defvar dired-find-subdir)
+;; `dired-find-subdir' is defined in `dired-x.el'

 ;; FIXME add a doc-string, and document dired-x extensions.
 (defun dired-find-buffer-nocreate (dirname &optional mode)
-  ;; This differs from dired-buffers-for-dir in that it does not consider
-  ;; subdirs of default-directory and searches for the first match only.
-  ;; Also, the major mode must be MODE.
-  (if (and (featurep 'dired-x)
-           dired-find-subdir
+  "Internal function for `dired-internal-noselect'.
+Searches among buffers of major-mode MODE (default `dired-mode')
+for one visiting DIRNAME, and returns the first one found. This
+differs from `dired-buffers-for-dir' in that it does not consider
+subdirs of `default-directory', it searches only for the first
+match, and it can restrict its search to buffers of
+major-mode MODE."
+  (if (and (featurep 'dired-x) dired-find-subdir
            ;; Don't try to find a wildcard as a subdirectory.
 	   (string-equal dirname (file-name-directory dirname)))
       (let* ((cur-buf (current-buffer))