From be97868f05c2bda95ab3c2d6348478d5d0306014 Mon Sep 17 00:00:00 2001 From: Allen Li 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