Subject: bug#46884: [PATCH] 27.1; Cannot run find-dired with -maxdepth

From: Allen Li <[email-masked]>
Date: Sun, 14 Mar 2021 00:40:36 +0000

On Sat, Mar 13, 2021 at 9:53 PM Juri Linkov <[email-masked]> wrote:
>
> > Sorry, I attached the wrong patch, here's the one for the rgrep approach.
>
> Thanks, I tried it, but for some reason 'C-u C-u find-dired RET'
> doesn't work.  Maybe because it uses 'grep-find-command' that is nil?

First patch was to outline intent to Eli; I didn't test it which was
an oversight on my part.

Here's a second patch which I've tested by running once each for zero,
one, and two prefix args.
If the approach looks good, I will test more, cleanup, update the
NEWS, docs, etc.

>
> > +With two \\[universal-argument] prefixes, directly edit and run `grep-find-command'.
> > ...
> > +    ((and grep-find-command (equal current-prefix-arg '(16)))
From be97868f05c2bda95ab3c2d6348478d5d0306014 Mon Sep 17 00:00:00 2001
From: Allen Li <[email-masked]>
Date: Fri, 12 Mar 2021 00:07:22 -0800
Subject: [PATCH] find-dired: Add command editing like rgrep

The original find-dired does not allow for constructing queries like

 find . -maxdepth 3 \( OTHER-ARGS \) -ls

* lisp/find-dired.el (find-dired): Added command editing and
confirmation.
---
 lisp/find-dired.el | 211 ++++++++++++++++++++++++++-------------------
 1 file changed, 121 insertions(+), 90 deletions(-)

diff --git a/lisp/find-dired.el b/lisp/find-dired.el
index adc5672eca..e4d2a5ba11 100644
--- a/lisp/find-dired.el
+++ b/lisp/find-dired.el
@@ -151,13 +151,16 @@ find-dired-refine-function
 (defvar find-args nil
   "Last arguments given to `find' by \\[find-dired].")
 
-;; History of find-args values entered in the minibuffer.
-(defvar find-args-history nil)
+(defvar find-args-history nil
+  "History list for args provided to `find-dired'.")
+
+(defvar find-dired-history nil
+  "History list for `find-dired'.")
 
 (defvar dired-sort-inhibit)
 
 ;;;###autoload
-(defun find-dired (dir args)
+(defun find-dired (dir &optional args confirm)
   "Run `find' and go into Dired mode on a buffer of the output.
 The command run (after changing into DIR) is essentially
 
@@ -166,93 +169,121 @@ find-dired
 except that the car of the variable `find-ls-option' specifies what to
 use in place of \"-ls\" as the final argument.
 
+With \\[universal-argument] prefix, you can edit the constructed shell command line
+before it is executed.
+With two \\[universal-argument] prefixes, directly edit and run `grep-find-command'.
+
 Collect output in the \"*Find*\" buffer.  To kill the job before
-it finishes, type \\[kill-find]."
-  (interactive (list (read-directory-name "Run find in directory: " nil "" t)
-		     (read-string "Run find (with args): " find-args
-				  '(find-args-history . 1))))
-  (let ((dired-buffers dired-buffers))
-    ;; Expand DIR ("" means default-directory), and make sure it has a
-    ;; trailing slash.
-    (setq dir (file-name-as-directory (expand-file-name dir)))
-    ;; Check that it's really a directory.
-    (or (file-directory-p dir)
-	(error "find-dired needs a directory: %s" dir))
-    (pop-to-buffer-same-window (get-buffer-create "*Find*"))
-
-    ;; See if there's still a `find' running, and offer to kill
-    ;; it first, if it is.
-    (let ((find (get-buffer-process (current-buffer))))
-      (when find
-	(if (or (not (eq (process-status find) 'run))
-		(yes-or-no-p
-		 (format-message "A `find' process is running; kill it? ")))
-	    (condition-case nil
-		(progn
-		  (interrupt-process find)
-		  (sit-for 1)
-		  (delete-process find))
-	      (error nil))
-	  (error "Cannot have two processes in `%s' at once" (buffer-name)))))
-
-    (widen)
-    (kill-all-local-variables)
-    (setq buffer-read-only nil)
-    (erase-buffer)
-    (setq default-directory dir
-	  find-args args	      ; save for next interactive call
-	  args (concat find-program " . "
-		       (if (string= args "")
-			   ""
-			 (concat
-			  (shell-quote-argument "(")
-			  " " args " "
-			  (shell-quote-argument ")")
-			  " "))
-		       (if (string-match "\\`\\(.*\\) {} \\(\\\\;\\|\\+\\)\\'"
-					 (car find-ls-option))
-			   (format "%s %s %s"
-				   (match-string 1 (car find-ls-option))
-				   (shell-quote-argument "{}")
-				   find-exec-terminator)
-			 (car find-ls-option))))
-    ;; Start the find process.
-    (shell-command (concat args "&") (current-buffer))
-    (dired-mode dir (cdr find-ls-option))
-    (let ((map (make-sparse-keymap)))
-      (set-keymap-parent map (current-local-map))
-      (define-key map "\C-c\C-k" 'kill-find)
-      (use-local-map map))
-    (setq-local dired-sort-inhibit t)
-    (setq-local revert-buffer-function
-                `(lambda (ignore-auto noconfirm)
-                   (find-dired ,dir ,find-args)))
-    ;; Set subdir-alist so that Tree Dired will work:
-    (if (fboundp 'dired-simple-subdir-alist)
-	;; will work even with nested dired format (dired-nstd.el,v 1.15
-	;; and later)
-	(dired-simple-subdir-alist)
-      ;; else we have an ancient tree dired (or classic dired, where
-      ;; this does no harm)
-      (setq-local dired-subdir-alist
-                  (list (cons default-directory (point-min-marker)))))
-    (setq-local dired-subdir-switches find-ls-subdir-switches)
-    (setq buffer-read-only nil)
-    ;; Subdir headlerline must come first because the first marker in
-    ;; subdir-alist points there.
-    (insert "  " dir ":\n")
-    ;; Make second line a ``find'' line in analogy to the ``total'' or
-    ;; ``wildcard'' line.
-    (let ((point (point)))
-      (insert "  " args "\n")
-      (dired-insert-set-properties point (point)))
-    (setq buffer-read-only t)
-    (let ((proc (get-buffer-process (current-buffer))))
-      (set-process-filter proc #'find-dired-filter)
-      (set-process-sentinel proc #'find-dired-sentinel)
-      ;; Initialize the process marker; it is used by the filter.
-      (move-marker (process-mark proc) (point) (current-buffer)))
-    (setq mode-line-process '(":%s"))))
+it finishes, type \\[kill-find].
+
+When called programmatically and ARGS is nil, DIR is expected to
+specify a command to run.
+
+If CONFIRM is non-nil, the user will be given an opportunity to edit the
+command before it's run."
+  (interactive
+   (cond
+    ((equal current-prefix-arg '(16))
+     (list (read-from-minibuffer "Run: " (cons (format "%s . -ls" find-program)
+                                               (+ (length find-program) 3))
+				 nil nil 'find-dired-history)))
+    (t (let* ((dir (read-directory-name "Run find in directory: " nil "" t))
+              (args (read-string "Run find (with args): " find-args
+                                 '(find-args-history . 1)))
+	      (confirm (equal current-prefix-arg '(4))))
+	 (list dir args confirm)))))
+  (let (command)
+    (if (null args)
+        (setq command dir
+              dir default-directory)
+      (setq find-args args	      ; save for next interactive call
+            command (concat find-program " . "
+		            (if (string= args "")
+		                ""
+		              (concat
+		               (shell-quote-argument "(")
+		               " " args " "
+		               (shell-quote-argument ")")
+		               " "))
+		            (if (string-match "\\`\\(.*\\) {} \\(\\\\;\\|\\+\\)\\'"
+				              (car find-ls-option))
+		                (format "%s %s %s"
+			                (match-string 1 (car find-ls-option))
+			                (shell-quote-argument "{}")
+			                find-exec-terminator)
+		              (car find-ls-option)))))
+    (if confirm
+        (setq command
+              (read-from-minibuffer "Confirm: "
+			            command nil nil 'find-dired-history))
+      (add-to-history 'find-dired-history command))
+    (let ((dired-buffers dired-buffers))
+      ;; Expand DIR ("" means default-directory), and make sure it has a
+      ;; trailing slash.
+      (setq dir (file-name-as-directory (expand-file-name dir)))
+      ;; Check that it's really a directory.
+      (or (file-directory-p dir)
+	  (error "find-dired needs a directory: %s" dir))
+      (pop-to-buffer-same-window (get-buffer-create "*Find*"))
+
+      ;; See if there's still a `find' running, and offer to kill
+      ;; it first, if it is.
+      (let ((find (get-buffer-process (current-buffer))))
+        (when find
+	  (if (or (not (eq (process-status find) 'run))
+		  (yes-or-no-p
+		   (format-message "A `find' process is running; kill it? ")))
+	      (condition-case nil
+		  (progn
+		    (interrupt-process find)
+		    (sit-for 1)
+		    (delete-process find))
+	        (error nil))
+	    (error "Cannot have two processes in `%s' at once" (buffer-name)))))
+
+      (widen)
+      (kill-all-local-variables)
+      (setq buffer-read-only nil)
+      (erase-buffer)
+      (setq default-directory dir)
+
+      ;; Start the find process.
+      (shell-command (concat command "&") (current-buffer))
+      (dired-mode dir (cdr find-ls-option))
+      (let ((map (make-sparse-keymap)))
+        (set-keymap-parent map (current-local-map))
+        (define-key map "\C-c\C-k" 'kill-find)
+        (use-local-map map))
+      (setq-local dired-sort-inhibit t)
+      (setq-local revert-buffer-function
+                  `(lambda (ignore-auto noconfirm)
+                     (find-dired ,dir ,find-args)))
+      ;; Set subdir-alist so that Tree Dired will work:
+      (if (fboundp 'dired-simple-subdir-alist)
+	  ;; will work even with nested dired format (dired-nstd.el,v 1.15
+	  ;; and later)
+	  (dired-simple-subdir-alist)
+        ;; else we have an ancient tree dired (or classic dired, where
+        ;; this does no harm)
+        (setq-local dired-subdir-alist
+                    (list (cons default-directory (point-min-marker)))))
+      (setq-local dired-subdir-switches find-ls-subdir-switches)
+      (setq buffer-read-only nil)
+      ;; Subdir headlerline must come first because the first marker in
+      ;; subdir-alist points there.
+      (insert "  " dir ":\n")
+      ;; Make second line a ``find'' line in analogy to the ``total'' or
+      ;; ``wildcard'' line.
+      (let ((point (point)))
+        (insert "  " command "\n")
+        (dired-insert-set-properties point (point)))
+      (setq buffer-read-only t)
+      (let ((proc (get-buffer-process (current-buffer))))
+        (set-process-filter proc #'find-dired-filter)
+        (set-process-sentinel proc #'find-dired-sentinel)
+        ;; Initialize the process marker; it is used by the filter.
+        (move-marker (process-mark proc) (point) (current-buffer)))
+      (setq mode-line-process '(":%s")))))
 
 (defun kill-find ()
   "Kill the `find' process running in the current buffer."
@@ -393,7 +424,7 @@ find-dired-sentinel
 	      ;; will stay around until M-x `list-processes'.
 	      (delete-process proc)
 	      (force-mode-line-update))))
-	  (message "find-dired %s finished." buf))))
+      (message "find-dired %s finished." buf))))
 
 (defun find-dired-sort-by-filename ()
   "Sort entries in *Find* buffer by file name lexicographically."
-- 
2.30.2