branch: elpa/dirvish commit 04a501732fdd32e1fc9424865006b70e330af539 Author: Alex Lu <hellosimon1...@hotmail.com> Commit: Alex Lu <hellosimon1...@hotmail.com>
feat(history): sort history entries with access time (#265) --- dirvish.el | 46 +++++++++++++++------------------ docs/CUSTOMIZING.org | 32 ++++++++++++++++------- docs/EXTENSIONS.org | 53 ++++++++++++++++++++++++++++++++------ extensions/dirvish-emerge.el | 16 ++++++------ extensions/dirvish-fd.el | 12 ++------- extensions/dirvish-history.el | 29 ++++++++++++++++++--- extensions/dirvish-quick-access.el | 1 - 7 files changed, 125 insertions(+), 64 deletions(-) diff --git a/dirvish.el b/dirvish.el index d123396a8a..e0c714dccb 100644 --- a/dirvish.el +++ b/dirvish.el @@ -25,8 +25,6 @@ (require 'cl-lib) (eval-when-compile (require 'project)) (declare-function ansi-color-apply-on-region "ansi-color") -(declare-function dirvish-fd-find "dirvish-fd") -(declare-function dirvish-tramp-noselect "dirvish-tramp") ;;;; User Options @@ -264,7 +262,7 @@ input for `dirvish-redisplay-debounce' seconds." "Functions called when directory data for the root buffer is ready." :group 'dirvish :type 'hook) -(defcustom dirvish-find-entry-hook '(dirvish-insert-entry-h) +(defcustom dirvish-find-entry-hook nil "Functions called before a Dired buffer is displayed." :group 'dirvish :type 'hook) @@ -466,15 +464,12 @@ ALIST is window arguments passed to `window--display-buffer'." "Get parent directory of PATH." (file-name-directory (directory-file-name (expand-file-name path)))) -(defun dirvish--append-metadata (metadata completions) - "Append METADATA for minibuffer COMPLETIONS." - (let ((entry (if (functionp metadata) - `(metadata (annotation-function . ,metadata)) - `(metadata (category . ,metadata))))) - (lambda (string pred action) - (if (eq action 'metadata) - entry - (complete-with-action action completions string pred))))) +(defun dirvish--completion-table-with-metadata (table metadata) + "Return new completion TABLE with METADATA, see `completion-metadata'." + (lambda (string pred action) + (if (eq action 'metadata) + `(metadata . ,metadata) + (complete-with-action action table string pred)))) (defun dirvish--change-selected (&rest _) "Record `dirvish--selected-window'." @@ -647,10 +642,15 @@ 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'." (let ((switch-to-buffer-preserve-window-point (null dired-auto-revert-buffer)) - (find-file-run-dired t) dv process-connection-type directory?) + (find-file-run-dired t) (dv (dirvish-curr)) + process-connection-type directory? buf) + (when (setq buf (and dv (alist-get entry (dv-roots dv) nil nil #'equal))) + (cl-return-from dirvish--find-entry + (dirvish-save-dedication (switch-to-buffer buf)))) (when (string-prefix-p "🔍" entry) - (dirvish-save-dedication (switch-to-buffer (dirvish-fd-find entry))) - (cl-return-from dirvish--find-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)))) (unless (setq directory? (file-directory-p entry)) (cl-loop with e = (downcase (or (file-name-extension entry) "")) for (es . (c . a)) in dirvish-open-with-programs @@ -659,8 +659,8 @@ filename or a string with format of `dirvish-fd-bufname'." (let ((a (cl-substitute entry "%f" a :test #'string=))) (when (eq find-fn 'find-alternate-file) (kill-current-buffer)) (apply #'start-process "" nil "nohup" (append (list c) a)))))) - (unless (setq dv (dirvish-curr)) ; for `find-dired', just forward it - (cl-return-from dirvish--find-entry (funcall find-fn entry))) + ;; forward requests from `find-dired' + (unless dv (cl-return-from dirvish--find-entry (funcall find-fn entry))) (when (and (dv-curr-layout dv) (eq find-fn 'find-file-other-window)) (user-error "Can not find a suitable other-window here")) (if directory? (dirvish-save-dedication (funcall find-fn entry)) @@ -702,8 +702,8 @@ filename or a string with format of `dirvish-fd-bufname'." (when reuse? (setf (dv-reuse dv) t)) (when new-buffer-p (if (not remote) (setq buffer (apply fn (list dir-or-list flags))) - (require 'dirvish-tramp) - (setq buffer (dirvish-tramp-noselect fn dir-or-list flags remote))) + (setq fn (prog1 'dirvish-tramp-noselect (require 'dirvish-tramp)) + buffer (apply fn (list dir-or-list flags remote)))) (with-current-buffer buffer (dirvish--setup-dired)) (push (cons key buffer) (dv-roots dv))) (with-current-buffer buffer @@ -725,6 +725,8 @@ filename or a string with format of `dirvish-fd-bufname'." do (dirvish-prop k (and (functionp v) (funcall v)))) (when bname (dired-goto-file bname)) (setf (dv-index dv) (cons key buffer)) + (let ((key (if (string-prefix-p "🔍" key) (buffer-name buffer) key))) + (setq dirvish--history (seq-take (push key dirvish--history) 200))) (run-hook-with-args 'dirvish-find-entry-hook key buffer) buffer))) @@ -757,12 +759,6 @@ filename or a string with format of `dirvish-fd-bufname'." (not (dirvish--get-session 'type 'peek))) (dirvish--preview-update dv filename))))))) -(defun dirvish-insert-entry-h (entry buffer) - "Add ENTRY or BUFFER name to `dirvish--history'." - (let ((entry (if (string-prefix-p "🔍" entry) - (buffer-name buffer) entry))) - (setq dirvish--history (seq-take (push entry dirvish--history) 200)))) - (defun dirvish-kill-buffer-h () "Remove buffer from session's buffer list." (when-let* ((dv (dirvish-curr)) (buf (current-buffer))) diff --git a/docs/CUSTOMIZING.org b/docs/CUSTOMIZING.org index 7c7ce84ac8..8fc6081577 100644 --- a/docs/CUSTOMIZING.org +++ b/docs/CUSTOMIZING.org @@ -28,8 +28,22 @@ Emacs to open video/audio files using =mpv=. ** Usage of GNU =ls= -Please use gnu =ls= (=gls= on macOS and freeBSD) as your =insert-directory-program=, -otherwise some features won't work correctly. +It is recommended to use GNU =ls= (referred to as =gls= on macOS and FreeBSD) as +your =insert-directory-program=. If you are using macOS, FreeBSD, or Windows, you +will need to manually install GNU =ls= and possibly configure it as your +=insert-directory-program=. On GNU/Linux systems, this program is typically +pre-installed and utilized by Emacs without requiring additional setup. + +#+begin_src bash +# macOS +brew install coreutils + +# FreeBSD +pkg install gnuls + +# Windows - install via Scoop: https://scoop.sh/ +scoop install coreutils +#+end_src To ensure Dirvish correctly parses =dired-listing-switches=, adhere to specific formatting rules when setting this variable. These rules ensure compatibility @@ -196,7 +210,7 @@ Dirvish offers out-of-the-box file previews for text files and directories. Furthermore, it provides interfaces and extensive user options to customize and expand its preview capabilities. -** Install dependency for better preview experience +** Install dependencies for an enhanced preview experience Dirvish offers file preview presets that depend on specific binaries. If you don't require these extra preview features, you can remove the corresponding @@ -214,28 +228,28 @@ no longer prompt you to install the associated programs. @@html:<h2>@@macOS@@html:</h2>@@ #+begin_src bash - brew install coreutils fd poppler ffmpegthumbnailer mediainfo vips 7zip +brew install poppler ffmpegthumbnailer mediainfo vips 7zip #+end_src @@html:<h2>@@Debian-based@@html:</h2>@@ #+begin_src bash - apt install fd-find poppler-utils ffmpegthumbnailer mediainfo libvips-tools +apt install poppler-utils ffmpegthumbnailer mediainfo libvips-tools #+end_src @@html:<h2>@@Arch-based@@html:</h2>@@ #+begin_src bash - pacman -S fd poppler ffmpegthumbnailer mediainfo libvips 7zip +pacman -S poppler ffmpegthumbnailer mediainfo libvips 7zip #+end_src @@html:<h2>@@FreeBSD@@html:</h2>@@ #+begin_src bash - pkg install gnuls fd-find poppler ffmpegthumbnailer vips 7-zip +pkg install poppler ffmpegthumbnailer vips 7-zip #+end_src @@html:<h2>@@Windows@@html:</h2>@@ #+begin_src bash - # install via Scoop: https://scoop.sh/ - scoop install coreutils fd poppler mtn libvips 7zip +# install via Scoop: https://scoop.sh/ +scoop install poppler mtn mediainfo libvips 7zip #+end_src @@html:</details>@@ diff --git a/docs/EXTENSIONS.org b/docs/EXTENSIONS.org index 99add81ec9..bb9a228673 100644 --- a/docs/EXTENSIONS.org +++ b/docs/EXTENSIONS.org @@ -197,6 +197,37 @@ breeze. No manual editing anymore! Please ensure [[https://github.com/sharkdp/fd][fd]] is installed on your OS before using this extension. +@@html:<details>@@ +@@html:<summary>@@ @@html:<b>@@Toggle install instructions@@html:</b>@@ @@html:</summary>@@ + +@@html:<h2>@@macOS@@html:</h2>@@ +#+begin_src bash +brew install fd +#+end_src + +@@html:<h2>@@Debian-based@@html:</h2>@@ +#+begin_src bash +apt install fd-find +#+end_src + +@@html:<h2>@@Arch-based@@html:</h2>@@ +#+begin_src bash +pacman -S fd +#+end_src + +@@html:<h2>@@FreeBSD@@html:</h2>@@ +#+begin_src bash +pkg install fd-find +#+end_src + +@@html:<h2>@@Windows@@html:</h2>@@ +#+begin_src bash +# install via Scoop: https://scoop.sh/ +scoop install fd +#+end_src + +@@html:</details>@@ + This is probably the BEST =fd= frontend, here is a quick demo: https://user-images.githubusercontent.com/16313743/170814774-98cc598d-6bc5-4fc3-9eea-21c98d6d4772.mp4 @@ -240,14 +271,20 @@ cursor as a subtree. * History navigation (dirvish-history.el) -|-----------------------------+---------------------------------------| -| Command | Description | -|-----------------------------+---------------------------------------| -| ~dirvish-history-jump~ | Go to recently visited directories | -| ~dirvish-history-go-forward~ | Go forward history (session locally) | -| ~dirvish-history-go-backward~ | Go backward history (session locally) | -| ~dirvish-history-last~ | Go to most recent used Dirvish buffer | -|-----------------------------+---------------------------------------| +This extension offers a collection of straightforward and useful history +navigation commands. + +*Commands*: + ++ ~dirvish-history-jump~ Go to recently visited directories ++ ~dirvish-history-go-forward~ Go forward history (session locally) ++ ~dirvish-history-go-backward~ Go backward history (session locally) ++ ~dirvish-history-last~ Go to most recent used Dirvish buffer + +*Options*: + ++ =dirvish-history-sort-function= + Sorting criteria for ~dirvish-history-jump~ command. * Quick keys for frequently visited places (dirvish-quick-access.el) diff --git a/extensions/dirvish-emerge.el b/extensions/dirvish-emerge.el index 2104596e62..b849273786 100644 --- a/extensions/dirvish-emerge.el +++ b/extensions/dirvish-emerge.el @@ -197,14 +197,14 @@ The predicate is consumed by `dirvish-emerge-groups'." "Read RECIPE from user input and optionally save it to OBJ." (ignore recipe) (let* ((table dirvish-emerge--available-preds) - (coll (dirvish--append-metadata - (lambda (i) - (let ((item (intern (format "%s" i)))) - (concat - (make-string - (- dirvish-emerge--max-pred-name-len (length i) -8) ?\s) - (cddr (assq item table))))) - table)) + (fn (lambda (i) + (let ((item (intern (format "%s" i)))) + (concat + (make-string + (- dirvish-emerge--max-pred-name-len (length i) -8) ?\s) + (cddr (assq item table)))))) + (coll (dirvish--completion-table-with-metadata + table `((annotation-function . ,fn)))) (pred (completing-read "Predicate: " coll))) (if obj (oset obj recipe `(predicate . ,(read pred))) (read pred)))) diff --git a/extensions/dirvish-fd.el b/extensions/dirvish-fd.el index f106b18a14..210b2bb406 100644 --- a/extensions/dirvish-fd.el +++ b/extensions/dirvish-fd.el @@ -283,7 +283,8 @@ value 16, let the user choose the root directory of their search." (default-directory base-dir) (output (shell-command-to-string command)) (files-raw (split-string output "\0" t)) - (files (dirvish--append-metadata 'file files-raw)) + (files (dirvish--completion-table-with-metadata + files-raw '((category . file)))) (file (completing-read "Go to: " files)) (full-file (concat remote file))) (dired-jump nil full-file)))) @@ -315,15 +316,6 @@ value 16, let the user choose the root directory of their search." (progn (insert f-full) (push (cons f-name f-full) res)) finally return (prog1 (nreverse res) (goto-char (point-min))))) -(defun dirvish-fd-find (entry) - "Run fd accroring to ENTRY." - (let* ((dv (dirvish-curr)) - (roots (and dv (dv-roots dv))) - (buf (and roots (alist-get entry roots nil nil #'equal)))) - (or buf - (pcase-let ((`(,pattern ,dir ,_) (split-string (substring entry 1) "📁"))) - (dirvish-fd dir pattern))))) - (defsubst dirvish-fd-revert (&rest _) "Revert buffer function for fd buffer." (dirvish-fd default-directory (or dirvish-fd--input ""))) diff --git a/extensions/dirvish-history.el b/extensions/dirvish-history.el index fa486ef2d4..f3570b64d6 100644 --- a/extensions/dirvish-history.el +++ b/extensions/dirvish-history.el @@ -16,14 +16,37 @@ (require 'dirvish) (require 'transient) +(defcustom dirvish-history-sort-function #'dirvish-history--sort-by-atime + "Function used to sort history entries for `dirvish-history-jump'." + :group 'dirvish :type 'function) + +(defun dirvish-history--sort-by-atime (file-list) + "Sort the FILE-LIST by access time, from most recent to least recent." + (thread-last + file-list + ;; Use modification time, since getting file access time seems to count as + ;; accessing the file, ruining future uses. + (mapcar (lambda (f) (cons f (file-attribute-access-time (file-attributes f))))) + (seq-sort (pcase-lambda (`(,f1 . ,t1) `(,f2 . ,t2)) + ;; Want existing, most recent, local files first. + (cond ((or (not (file-exists-p f1)) (file-remote-p f1)) nil) + ((or (not (file-exists-p f2)) (file-remote-p f2)) t) + (t (time-less-p t2 t1))))) + (mapcar #'car))) + ;;;###autoload (defun dirvish-history-jump () "Open a target directory from `dirvish--history'." (interactive) (unless dirvish--history (user-error "Dirvish[error]: no history entries")) - (let* ((entries (dirvish--append-metadata 'file dirvish--history)) - (result (completing-read "Recently visited: " entries))) - (when result (dirvish--find-entry 'find-file result)))) + (when-let* ((result + (completing-read + "Recently visited: " + (dirvish--completion-table-with-metadata + dirvish--history + `((category . file) + (display-sort-function . ,dirvish-history-sort-function)))))) + (dirvish--find-entry 'find-file result))) ;;;###autoload (defun dirvish-history-last () diff --git a/extensions/dirvish-quick-access.el b/extensions/dirvish-quick-access.el index ed51d28fae..e8ccad46db 100644 --- a/extensions/dirvish-quick-access.el +++ b/extensions/dirvish-quick-access.el @@ -65,7 +65,6 @@ Here is a sample value for this variable. (interactive) (funcall dirvish-quick-access-function ,path))))] (interactive) - (require 'dirvish-fd nil t) (transient-setup 'dirvish-quick-access)))))) (provide 'dirvish-quick-access)