Subject: Re: bug#79293: [PATCH] Pass dired default filenames via defaults argument

From: Spencer Baugh <[email-masked]>
Date: Wed, 27 Aug 2025 16:26:17 -0400

Juri Linkov <[email-masked]> writes:

>> Also, attached is an updated version of the patch which reflects the
>> fact that the "default" variable actually was unused before, only
>> "defaults" was used.
>> [...]
>> -	   (default (and dired-one-file
>> -		         (not dired-dwim-target) ; Bug#25609
>
> Removing these lines breaks dired-tests added
> in the same commit 841e3e377c97 for Bug#25609 above.

Ah, sorry, I guess I forgot to run dired-tests.  But this is great news,
this means this code is covered by at least one test.

The real issue in bug#25609 is that the minibuffer default didn't have
the correct value when dired-dwim-target is non-nil.  From my analysis
of the code, the default should always be the same as the initial input
in the minibuffer (TARGET-DIR), but that wasn't happening with non-nil
dired-dwim-target.

With my change, this is fixed easily enough by just making sure
TARGET-DIR is the first element of the defaults list.  This doesn't
change observable behavior because read-file-name explicitly handles the
case where the initial input matches the first default:

  ;; When the first default in `minibuffer-default'
  ;; duplicates initial input `insdef',
  ;; reset `minibuffer-default' to nil.
  (when (equal (or (car-safe insdef) insdef)
               (or (car-safe minibuffer-default)
                   minibuffer-default))
    (setq minibuffer-default
          (cdr-safe minibuffer-default)))

This also fixes bug#25609, with even less code, and dired-tests and
dired-aux-tests both once again all pass.

This version of the patch also extends the change to
dired-compare-directories, so it gets the abbreviate-file-name benefits
too.

>From 388b1855afd2252431c8b44e6674362c6219c18d Mon Sep 17 00:00:00 2001
From: Spencer Baugh <[email-masked]>
Date: Fri, 22 Aug 2025 17:19:09 -0400
Subject: [PATCH] Pass dired default filenames via defaults argument

Rather than using minibuffer-with-setup-hook, just pass the list
of default file names as a regular argument to read-file-name.
This allows read-file-name to run abbreviate-file-name on the
defaults as it normally does, instead of the defaults appearing
in expanded form.

dired-dwim-target-defaults changes slightly to return the
correct default at the start of the list.

* lisp/dired-aux.el (dired-do-create-files)
(dired-compare-directories): Pass default file names as an
argument. (bug#79293)
(dired-dwim-target-defaults): Return the correct default at the
start of the list.
---
 lisp/dired-aux.el | 51 +++++++++++++++++++----------------------------
 1 file changed, 20 insertions(+), 31 deletions(-)

diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el
index 049d200f590..e28106d9865 100644
--- a/lisp/dired-aux.el
+++ b/lisp/dired-aux.el
@@ -330,14 +330,13 @@ dired-compare-directories
   (interactive
    (list
     (let* ((target-dir (dired-dwim-target-directory))
-	   (defaults (dired-dwim-target-defaults nil target-dir)))
+           (defaults (dired-dwim-target-defaults nil target-dir)))
       (minibuffer-with-setup-hook
 	  (lambda ()
-            (setq-local minibuffer-default-add-function nil)
-	    (setq minibuffer-default defaults))
+            (setq-local minibuffer-default-add-function nil))
 	(read-directory-name (format "Compare %s with: "
 				     (dired-current-directory))
-			     target-dir target-dir t)))
+			     target-dir defaults t)))
     (read-from-minibuffer "Mark if (lisp expr or RET): " nil nil t nil "nil"))
    dired-mode)
   (let* ((dir1 (dired-current-directory))
@@ -2668,17 +2667,12 @@ dired-do-create-files
 	   (dired-one-file	; fluid variable inside dired-create-files
 	    (and (consp fn-list) (null (cdr fn-list)) (car fn-list)))
 	   (target-dir (dired-dwim-target-directory))
-	   (default (and dired-one-file
-		         (not dired-dwim-target) ; Bug#25609
-		         (expand-file-name (file-name-nondirectory
-                                            (car fn-list))
-					   target-dir)))
 	   (defaults (dired-dwim-target-defaults fn-list target-dir))
 	   (target (expand-file-name ; fluid variable inside dired-create-files
 		    (minibuffer-with-setup-hook
 		        (lambda ()
-                          (setq-local minibuffer-default-add-function nil)
-			  (setq minibuffer-default defaults))
+                          ;; Don't run `read-file-name--defaults'
+                          (setq-local minibuffer-default-add-function nil))
 		      (dired-mark-read-file-name
                        (format "%s %%s %s: "
                                (if dired-one-file op1 operation)
@@ -2688,7 +2682,7 @@ dired-do-create-files
                                    ;; other operations copy (etc) to the
                                    ;; prompted file name.
                                    "from" "to"))
-		       target-dir op-symbol arg rfn-list default))))
+		       target-dir op-symbol arg rfn-list defaults))))
 	   (into-dir
             (progn
               (when
@@ -2813,28 +2807,26 @@ dired-dwim-target-directory
       this-dir)))
 
 (defun dired-dwim-target-defaults (fn-list target-dir)
-  ;; Return a list of default values for file-reading functions in Dired.
-  ;; This list may contain directories from Dired buffers in other windows.
-  ;; `fn-list' is a list of file names used to build a list of defaults.
-  ;; When nil or more than one element, a list of defaults will
-  ;; contain only directory names.  `target-dir' is a directory name
-  ;; to exclude from the returned list, for the case when this
-  ;; directory name is already presented in initial input.
-  ;; For Dired operations that support `dired-dwim-target',
-  ;; the argument `target-dir' should have the value returned
-  ;; from `dired-dwim-target-directory'.
+  "Return a list of default values for file-reading functions in Dired.
+
+This list may contain directories from Dired buffers in other windows.
+FN-LIST is a list of file names used to build a list of defaults.
+When nil or more than one element, a list of defaults will
+contain only directory names.
+
+TARGET-DIR should be the initial input in the minibuffer for the
+file-reading function.  For Dired operations that support
+`dired-dwim-target', TARGET-DIR should have the value returned from
+`dired-dwim-target-directory'."
   (let ((dired-one-file
 	 (and (consp fn-list) (null (cdr fn-list)) (car fn-list)))
 	(current-dir (and (eq major-mode 'dired-mode)
 			  (dired-current-directory)))
 	;; Get a list of directories of visible buffers in dired-mode.
 	(dired-dirs (dired-dwim-target-directories)))
-    ;; Force the current dir to be the first in the list.
+    ;; Force TARGET-DIR then CURRENT-DIR to be first in the list.
     (setq dired-dirs
-	  (delete-dups (delq nil (cons current-dir dired-dirs))))
-    ;; Remove the target dir (if specified) or the current dir from
-    ;; default values, because it should be already in initial input.
-    (setq dired-dirs (delete (or target-dir current-dir) dired-dirs))
+	  (delete-dups (delq nil (cons target-dir (cons current-dir dired-dirs)))))
     ;; Return a list of default values.
     (if dired-one-file
 	;; For one file operation, provide a list that contains
@@ -2847,10 +2839,7 @@ dired-dwim-target-defaults
 		(mapcar (lambda (dir)
 			  (expand-file-name
 			   (file-name-nondirectory (car fn-list)) dir))
-			(reverse dired-dirs))
-		(list (expand-file-name
-		       (file-name-nondirectory (car fn-list))
-		       (or target-dir current-dir))))
+			(reverse dired-dirs)))
       ;; For multi-file operation, return only a list of other directories.
       dired-dirs)))
 
-- 
2.43.7