branch: elpa/dirvish commit 081b8df9c723f84d25faa65ef5c3a80caeb8780f Author: Alex Lu <hellosimon1...@hotmail.com> Commit: Alex Lu <hellosimon1...@hotmail.com>
feat: allow opening large directory with `fd` automatically --- dirvish-widgets.el | 48 ++++++++++++++----------- dirvish.el | 51 ++++++++++++++------------- extensions/dirvish-fd.el | 92 ++++++++++++++++++++++-------------------------- 3 files changed, 96 insertions(+), 95 deletions(-) diff --git a/dirvish-widgets.el b/dirvish-widgets.el index 95cb3b14d8..3062213bdf 100644 --- a/dirvish-widgets.el +++ b/dirvish-widgets.el @@ -45,6 +45,10 @@ This value is passed to function `format-time-string'." :group 'dirvish :type 'string) +(defcustom dirvish-file-count-overflow 15000 + "Up limit for counting directory files, to improve performance." + :group 'dirvish :type 'natnum) + (defcustom dirvish-path-separators '(" ⌂" " ∀" " ⋗ ") "Separators in path mode line segment. The value is a list with 3 elements: @@ -190,18 +194,20 @@ Audio;(Audio-codec . \"\"%CodecID%\"\")(Audio-bitrate . \"\"%BitRate/String%\"\" (defun dirvish--attr-size-human-readable (file-size kilo) "Produce a string showing FILE-SIZE in human-readable form. KILO is 1024.0 / 1000 for file size / counts respectively." - (let ((prefixes '("" "k" "M" "G" "T" "P" "E" "Z" "Y"))) - (while (and (>= file-size kilo) (cdr prefixes)) - (setq file-size (/ file-size kilo) - prefixes (cdr prefixes))) - (substring (format (if (and (< file-size 10) - (>= (mod file-size 1.0) 0.05) - (< (mod file-size 1.0) 0.95)) - " %.1f%s%s" - " %.0f%s%s") - file-size (car prefixes) - (if (dirvish-prop :gui) " " "")) - -6))) + (if (and (eq kilo 1000) (> file-size (- dirvish-file-count-overflow 3))) + " MANY " + (let ((prefixes '("" "k" "M" "G" "T" "P" "E" "Z" "Y"))) + (while (and (>= file-size kilo) (cdr prefixes)) + (setq file-size (/ file-size kilo) + prefixes (cdr prefixes))) + (substring (format (if (and (< file-size 10) + (>= (mod file-size 1.0) 0.05) + (< (mod file-size 1.0) 0.95)) + " %.1f%s%s" + " %.0f%s%s") + file-size (car prefixes) + (if (dirvish-prop :gui) " " "")) + -6)))) (defun dirvish--file-attr-size (name attrs) "Get file size of file NAME from ATTRS." @@ -211,20 +217,22 @@ KILO is 1024.0 / 1000 for file size / counts respectively." (if (dirvish-prop :gui) " " "")) -6)) ((stringp (file-attribute-type attrs)) - (let ((ct (dirvish-attribute-cache name :f-count - (condition-case nil - (let ((files (directory-files name nil nil t))) - (dirvish--attr-size-human-readable - (- (length files) 2) 1000)) - (file-error 'file))))) + (let* ((ovfl dirvish-file-count-overflow) + (ct (dirvish-attribute-cache name :f-count + (condition-case nil + (let ((files (directory-files name nil nil t ovfl))) + (dirvish--attr-size-human-readable + (- (length files) 2) 1000)) + (file-error 'file))))) (if (not (eq ct 'file)) ct (dirvish-attribute-cache name :f-size (dirvish--attr-size-human-readable (file-attribute-size (file-attributes name)) 1024.0))))) ((file-attribute-type attrs) - (let ((ct (dirvish-attribute-cache name :f-count + (let* ((ovfl dirvish-file-count-overflow) + (ct (dirvish-attribute-cache name :f-count (condition-case nil - (let ((files (directory-files name nil nil t))) + (let ((files (directory-files name nil nil t ovfl))) (dirvish--attr-size-human-readable (- (length files) 2) 1000)) (file-error 'no-permission))))) diff --git a/dirvish.el b/dirvish.el index 59cbe2a382..aa8c413b0d 100644 --- a/dirvish.el +++ b/dirvish.el @@ -102,6 +102,11 @@ the full-frame layout when file previews are needed." (float :tag "max width of parent windows") (float :tag "width of preview windows")))) +(defcustom dirvish-large-directory-threshold nil + "Directories with file count greater than this are opened using `dirvish-fd'." + :group 'dirvish :type '(choice (const :tag "Never use `dirvish-fd'" nil) + (natnum :tag "File counts in integer"))) + (defface dirvish-hl-line '((t :inherit highlight :extend t)) "Face used for Dirvish line highlighting in focused Dirvish window." @@ -602,18 +607,10 @@ FROM-QUIT is used to signify the calling command." (cl-defun dirvish--find-entry (find-fn entry) "Find ENTRY using FIND-FN in current dirvish session. FIND-FN can be one of `find-file', `find-alternate-file', -`find-file-other-window' or `find-file-other-frame'. ENTRY can be a -filename or a string with format of `dirvish-fd-bufname'." +`find-file-other-window' or `find-file-other-frame'." (let ((switch-to-buffer-preserve-window-point (null dired-auto-revert-buffer)) (find-file-run-dired t) (dv (dirvish-curr)) - (directory? (file-directory-p entry)) (cur (current-buffer)) cache) - (when (setq cache (and dv (alist-get entry (dv-roots dv) nil nil #'equal))) - (cl-return-from dirvish--find-entry - (dirvish-save-dedication (switch-to-buffer cache)))) - (when (string-prefix-p "🔍" entry) - (setq find-fn (prog1 'dirvish-fd (require 'dirvish-fd nil t))) - (pcase-let ((`(,re ,dir ,_) (split-string (substring entry 1) "📁"))) - (cl-return-from dirvish--find-entry (funcall find-fn dir re)))) + (dir? (file-directory-p entry)) (cur (current-buffer))) (and (run-hook-with-args-until-success 'dirvish-find-entry-hook entry find-fn) (cl-return-from dirvish--find-entry)) @@ -621,13 +618,13 @@ filename or a string with format of `dirvish-fd-bufname'." (unless dv (cl-return-from dirvish--find-entry (funcall find-fn entry))) (and (dv-curr-layout dv) (eq find-fn 'find-file-other-window) (dirvish-layout-toggle)) - (when (and directory? (eq find-fn 'find-alternate-file)) + (when (and dir? (eq find-fn 'find-alternate-file)) (dirvish-save-dedication (find-file entry)) (with-current-buffer cur ; check if the buffer should be killed (and (bound-and-true-p server-buffer-clients) (cl-return-from dirvish--find-entry))) (cl-return-from dirvish--find-entry (dirvish--kill-buffer cur))) - (if directory? (dirvish-save-dedication (funcall find-fn entry)) + (if dir? (dirvish-save-dedication (funcall find-fn entry)) (mapc #'dirvish--kill-buffer (dv-preview-buffers dv)) (funcall (dv-open-file dv) dv find-fn entry)))) @@ -1171,13 +1168,13 @@ Optionally, use CURSOR as the enabled cursor type." do (push b rs) ; in case there is any lingering sessions finally do (unless rs (setq dirvish--sessions (dirvish--ht))))))) -(defun dirvish--setup-dired (&optional revert-fn) - "Initialize Dired buffers, set `revert-buffer-function' to REVERT-FN." +(defun dirvish--setup-dired () + "Initialize Dired buffers." (use-local-map dirvish-mode-map) (dirvish--hide-dired-header) (dirvish--maybe-toggle-cursor 'box) ; restore from `wdired' (setq-local dirvish--dir-data (or dirvish--dir-data (dirvish--ht)) - revert-buffer-function (or revert-fn #'dirvish-revert) + revert-buffer-function (or (dirvish-prop :revert) #'dirvish-revert) truncate-lines t dired-hide-details-hide-symlink-targets nil) (add-hook 'pre-redisplay-functions #'dirvish-pre-redisplay-h nil t) (add-hook 'window-buffer-change-functions #'dirvish-winbuf-change-h nil t) @@ -1469,25 +1466,29 @@ With optional NOSELECT just find files but do not select them." (mapc #'dirvish--kill-buffer (dv-preview-buffers dv)) (dired-simultaneous-find-file files noselect))) -(defun dirvish-dired-noselect-a (fn dir-or-list &optional flags) +(defun dirvish-dired-noselect-a (fn dir-or-list &optional flags re) "Return buffer for DIR-OR-LIST with FLAGS, FN is `dired-noselect'." (let* ((dir (if (consp dir-or-list) (car dir-or-list) dir-or-list)) (key (file-name-as-directory (expand-file-name dir))) (dv (or (dirvish-curr) (dirvish--get-session) (dirvish--new))) - (bname buffer-file-name) - (remote (file-remote-p dir)) + (bname buffer-file-name) (remote (file-remote-p dir)) (flags (or flags (dv-ls-switches dv))) + (mc dirvish-large-directory-threshold) (buffer (alist-get key (dv-roots dv) nil nil #'equal)) (new? (null buffer)) (dps (dv-preview-dispatchers dv)) - tramp-fn dired-buffers) ; disable reuse from dired + tramp fd dired-buffers) ; disable reuse from dired (setf (dv-timestamp dv) (dirvish--timestamp)) - (when new? - (if (not remote) (setq buffer (apply fn (list dir-or-list flags))) - (setq tramp-fn (prog1 'dirvish-tramp-noselect (require 'dirvish-tramp)) - buffer (apply tramp-fn (list fn dir-or-list flags remote dps)))) - (with-current-buffer buffer (dirvish--setup-dired)) - (push (cons key buffer) (dv-roots dv))) + (cond ((and new? remote) + (setq tramp (prog1 'dirvish-tramp-noselect (require 'dirvish-tramp)) + buffer (apply tramp (list fn dir-or-list flags remote dps)))) + ((or re (and mc (length> (directory-files key nil nil t mc) (1- mc)))) + (setq fd (prog1 'dirvish-fd-noselect (require 'dirvish-fd nil t)) + buffer (apply fd (list dv key (or re ""))) + key (concat key "🔍" (or re "")))) + (new? (setq buffer (apply fn (list dir-or-list flags))))) + (cl-pushnew (cons key buffer) (dv-roots dv) :test #'equal) (with-current-buffer buffer + (dirvish--setup-dired) (cond (new? nil) ((and (not remote) (not (equal flags dired-actual-switches))) (dired-sort-other flags)) diff --git a/extensions/dirvish-fd.el b/extensions/dirvish-fd.el index 6bbfe92fa8..cfeca5fea7 100644 --- a/extensions/dirvish-fd.el +++ b/extensions/dirvish-fd.el @@ -61,10 +61,9 @@ "Listing program for `fd'." :type '(string :tag "Listing program, such as `ls'") :group 'dirvish) - -(defconst dirvish-fd-bufname "🔍%s📁%s📁") ; TODO: remove this -(defconst dirvish-fd-header ; TODO: refactor this - (dirvish--mode-line-composer '(fd-info) '(fd-status) t)) +(defcustom dirvish-fd-header-line-format '(:left (fd-info) :right (fd-status)) + "Header line format for `dirvish-fd'." + :group 'dirvish :type 'plist) (defun dirvish-fd--ensure-fd (remote) "Return fd executable on REMOTE or localhost. @@ -275,65 +274,58 @@ Raise an error if fd executable is not available." (process-put proc 'start (float-time)) (process-put proc 'target (current-buffer)))) +(defun dirvish-fd-noselect (dv dir pattern) + "Return the fd buffer for DV at DIR with search PATTERN." + (let* ((re (mapcan (lambda (x) `(,(format "--and=%s" x))) + (if (stringp pattern) (split-string pattern ",") pattern))) + (ls-switches (or dired-actual-switches (dv-ls-switches dv))) + (key (file-name-nondirectory (directory-file-name dir))) + (buf (get-buffer-create (concat key "🔍" pattern "🔍" (dv-id dv)))) + (fd (dirvish-prop :fd-info)) (re (or re (cadr fd))) + (switches (or (cddr fd) (split-string dirvish-fd-switches)))) + (with-current-buffer buf + (let (buffer-read-only) + (erase-buffer) + (insert " " dir ":" (make-string (dirvish--subdir-offset) ?\n))) + (unless (derived-mode-p 'dired-mode) (dired-mode dir ls-switches)) + (setq-local default-directory dir + dired-subdir-alist (list (cons dir (point-min-marker)))) + (dirvish-fd--argparser re switches) + (dirvish-prop :revert + (lambda (&rest _) + (setq dired-subdir-alist (list (car (reverse dired-subdir-alist)))) + (let (buffer-read-only) + (buffer-disable-undo) + (delete-region (goto-char (dirvish-prop :content-begin)) (point-max))) + (buffer-enable-undo) + (dirvish-fd--start-proc))) + (let* ((fmt dirvish-fd-header-line-format) + (l (plist-get fmt :left)) (r (plist-get fmt :right))) + (dirvish-prop :cus-header (dirvish--mode-line-composer l r t))) + (dirvish-prop :global-header t) + (dirvish--setup-dired) + (dirvish-fd--start-proc) buf))) + ;;;###autoload -(defun dirvish-fd (dir re) +(defun dirvish-fd (dir pattern) "Run `fd' on DIR and go into Dired mode on a buffer of the output. The command run is essentially: fd --color=never `dirvish-fd-switches' - --and RE [--and RE1 --and RE2 … ] + --and PATTERN [--and PATTERN1 --and PATTERN2 … ] --exec-batch `dirvish-fd-ls-program' `dired-listing-switches' --directory If called with \\`C-u', prompt for the target directory, `default-directory' is used. If prefixed with \\`C-u' twice, also -prompt for the search regex RE as a comma separated list." +prompt for the search regex PATTERN as a comma separated list." (interactive (list (and current-prefix-arg (read-directory-name "Fd target directory: " nil "" t)) (and (equal current-prefix-arg '(16)) (completing-read-multiple "Pattern: " nil)))) - (setq dir (file-name-as-directory - (expand-file-name (or dir default-directory))) - re (mapcan (lambda (x) `(,(format "--and=%s" x))) - (if (stringp re) (split-string re ",") re))) - (or (file-directory-p dir) (user-error "'fd' requires a directory: %s" dir)) - (let* ((dv (or (dirvish-curr) (dirvish--get-session) (dirvish--new))) - (ls-switches (or dired-actual-switches (dv-ls-switches dv))) - (root (format dirvish-fd-bufname (or re "") - (file-name-nondirectory (directory-file-name dir)))) - (buffer (get-buffer-create (concat root (dirvish--timestamp)))) - (fd (dirvish-prop :fd-info)) (re (or re (cadr fd))) - (switches (or (cddr fd) (split-string dirvish-fd-switches))) - remote) - (setf (dv-index dv) (cons root buffer)) - (cl-pushnew (cons root buffer) (dv-roots dv) :test #'equal) - (with-current-buffer buffer ; TODO: session independent? - (let (buffer-read-only) (erase-buffer)) - (insert " " dir ":" (make-string (dirvish--subdir-offset) ?\n)) - (dired-mode dir ls-switches) - (setq-local default-directory dir - dired-subdir-alist (list (cons dir (point-min-marker)))) - (dirvish--setup-dired - (lambda (&rest _) - (setq dired-subdir-alist (list (car (reverse dired-subdir-alist)))) - (let (buffer-read-only) - (buffer-disable-undo) - (delete-region (goto-char (dirvish-prop :content-begin)) (point-max))) - (buffer-enable-undo) - (dirvish-fd--start-proc))) - (dirvish-fd--argparser re switches) - (dirvish-prop :root root) - (dirvish-prop :dv (dv-id dv)) - (dirvish-prop :gui (display-graphic-p)) ; TODO: remove this - (dirvish-prop :cus-header 'dirvish-fd-header) - (dirvish-prop :remote (setq remote (file-remote-p dir))) - (dirvish-prop :global-header t) - (dirvish-prop :preview-dps (unless remote (dv-preview-dispatchers dv))) - (dirvish-prop :attrs (dv-attributes dv)) - (cl-loop for (k v) on dirvish--scopes by 'cddr - do (dirvish-prop k (and (functionp v) (funcall v)))) - (dirvish-save-dedication - (switch-to-buffer buffer) (dirvish--build-layout dv)) - (dirvish-fd--start-proc)))) + (let* ((dir (or dir default-directory)) + (buf (dirvish-dired-noselect-a nil dir nil (or pattern ""))) + (dv (with-current-buffer buf (dirvish-curr)))) + (dirvish-save-dedication (switch-to-buffer buf) (dirvish--build-layout dv)))) (define-obsolete-function-alias 'dirvish-fd-ask #'dirvish-fd "Apr 4, 2025")