branch: elpa/dirvish commit a41932a9607168cd2ff7dbbce79bbe654255cac0 Author: Alex Lu <hellosimon1...@hotmail.com> Commit: Alex Lu <hellosimon1...@hotmail.com>
feat: session|buffer locally attributes and mode-line-format (#286) Closes #286 --- dirvish-extras.el | 6 +-- dirvish-widgets.el | 27 ++++++++++++- dirvish.el | 94 +++++++++++++++++++--------------------------- extensions/dirvish-side.el | 33 ++++++++-------- extensions/dirvish-vc.el | 3 +- 5 files changed, 84 insertions(+), 79 deletions(-) diff --git a/dirvish-extras.el b/dirvish-extras.el index aa5f50f9f6..cb84852688 100644 --- a/dirvish-extras.el +++ b/dirvish-extras.el @@ -83,10 +83,8 @@ RECIPE has the same form as `dirvish-default-layout'." (remq item old-val)))) (mapc #'require '(dirvish-widgets dirvish-vc dirvish-collapse)) (dirvish--render-attrs 'clear) - (setq-local dirvish-attributes new-val) - (setq-local dirvish--working-attrs - (dirvish--attrs-expand - (append '(hl-line symlink-target) new-val))) + (dirvish-prop :attrs + (dirvish--attrs-expand (append '(hl-line symlink-target) new-val))) (dirvish--render-attrs))) ;;;###autoload (autoload 'dirvish-setup-menu "dirvish-extras" nil t) diff --git a/dirvish-widgets.el b/dirvish-widgets.el index b417128a9c..d9c0f6032c 100644 --- a/dirvish-widgets.el +++ b/dirvish-widgets.el @@ -16,13 +16,25 @@ ;; `file-size', `file-time' ;; ;; Mode-line segments: +;; ;; `path', `symlink', `omit', `sort', `index', `free-space', `file-link-number', ;; `file-user', `file-group', `file-time', `file-size', `file-modes', ;; `file-inode-number', `file-device-number' ;; ;; Preview dispatchers: -;; `audio' `image', `gif', `video', `video-mtn', `epub', `archive', `pdf', `pdf-preface' -;; TODO: add `image-dired' preview dispatcher +;; +;; - `image': preview image files, requires `imagemagick' +;; - `gif': preview GIF image files with animation +;; - `video': preview videos files with thumbnail image +;; - requires `ffmpegthumbnailer' on Linux/macOS +;; - requires `mtn' on Windows (special thanks to @samb233!) +;; - `audio': preview audio files with metadata, requires `mediainfo' +;; - `epub': preview epub documents, requires `epub-thumbnail' +;; - `pdf': preview pdf documents via `pdf-tools' +;; - `archive': preview archive files, requires `tar' and `unzip' +;; - `dired': preview directories using `dired' (asynchronously) +;; - `pdf-preface': preview pdf documents with thumbnail image, require `pdftoppm' +;; - `image-dired' NOT implemented yet | TODO ;;; Code: @@ -476,6 +488,17 @@ GROUP-TITLES is a list of group titles." (pcase-let ((`(,attr . ,face) (dirvish--format-file-attr 'device-number))) (propertize (format "%s" attr) 'face face))) +(dirvish-define-mode-line project + "Return a string showing current project." + (let ((project (dirvish--get-project-root)) + (face (if (dirvish--selected-p) 'dired-header 'dirvish-inactive))) + (if project + (setq project (file-name-base (directory-file-name project))) + (setq project "-")) + (format " %s %s" + (propertize "Project:" 'face face) + (propertize project 'face 'font-lock-string-face)))) + ;;;; Preview dispatchers (cl-defmethod dirvish-build-cache (&context ((display-graphic-p) (eql t))) diff --git a/dirvish.el b/dirvish.el index 39cf19048b..3066a37d2e 100644 --- a/dirvish.el +++ b/dirvish.el @@ -265,33 +265,25 @@ input for `dirvish-redisplay-debounce' seconds." (defvar dirvish-scopes '(:frame selected-frame :tab tab-bar--current-tab-index :persp persp-curr)) -(defvar dirvish-libraries - '((dirvish-widgets path symlink sort omit index free-space file-link-number - file-user file-group file-time file-size file-modes - file-inode-number file-device-number - audio image gif video epub pdf pdf-preface archive) - (dirvish-vc vc-state git-msg vc-diff vc-blame vc-log vc-info) - (dirvish-icons all-the-icons nerd-icons vscode-icon) - (dirvish-collapse collapse) - (dirvish-subtree subtree-state) - (dirvish-yank yank))) (defvar dirvish-mode-map (let ((map (make-sparse-keymap))) (set-keymap-parent map dired-mode-map) (define-key map (kbd "q") 'dirvish-quit) map) "Keymap used in dirvish buffers.") +(defvar dirvish--libraries + '((dirvish-vc vc-state git-msg vc-diff vc-blame vc-log vc-info) + (dirvish-icons all-the-icons nerd-icons vscode-icon) + (dirvish-collapse collapse) + (dirvish-subtree subtree-state) + (dirvish-yank yank))) (defvar dirvish-redisplay-debounce-timer nil) (defvar dirvish--history nil) (defvar dirvish--reset-keywords '(:free-space :content-begin)) (defvar dirvish--selected-window nil) -(defvar dirvish--mode-line-fmt nil) -(defvar dirvish--header-line-fmt nil) (defvar dirvish--session-hash (make-hash-table :test #'equal)) (defvar dirvish--parent-hash (make-hash-table :test #'equal)) (defvar dirvish--available-attrs '()) (defvar dirvish--available-preview-dispatchers '()) -(defvar dirvish--working-attrs '()) -(defvar dirvish--working-preview-dispathchers '()) (defvar image-dired-thumbnail-buffer) (defvar server-buffer-clients) (defvar-local dirvish--props '()) @@ -520,6 +512,10 @@ Set process's SENTINEL and PUTS accordingly." (ff-layout dirvish-default-layout :documentation "is a full-frame layout recipe.") (reuse () :documentation "indicates if DV has been reused.") (ls-switches dired-listing-switches :documentation "is the directory listing switches.") + (mode-line () :documentation "is the `mode-line-format' used by this DV.") + (header-line () :documentation "is the `header-line-format' used by DV.") + (preview-dispatchers () :documentation "is the working preview methods of DV.") + (attributes () :documentation "is the working attributes of DV.") (preview-buffers () :documentation "holds all file preview buffers of DV.") (preview-window () :documentation "is the window to display preview buffer.") (winconf () :documentation "is a saved window configuration.") @@ -535,7 +531,7 @@ ARGS is a list of keyword arguments for `dirvish' struct." ;; ensure we have a fallback fullframe layout (unless dirvish-default-layout (setf (dv-ff-layout new) '(1 0.11 0.55))) (puthash (dv-id new) new dirvish--session-hash) - (dirvish--check-deps) + (dirvish--check-dependencies new) (dirvish--create-root-window new) new)) (defun dirvish--get-session (&optional key val) @@ -603,31 +599,28 @@ FROM-QUIT is used to signify the calling command." finally return (reverse res))) (defun dirvish--attrs-expand (attrs) - "Expand ATTRS to `dirvish--working-attrs'." + "Expand ATTRS from `dirvish--available-attrs'." (sort (cl-loop for attr in attrs for lst = (alist-get attr dirvish--available-attrs) for (idx width pred render ov _) = lst collect (list idx (eval width) pred render ov)) (lambda (a b) (< (car a) (car b))))) -(defun dirvish--check-deps () - "Remove invalid widgets, raise warnings for missing dependencies." +(defun dirvish--check-dependencies (dv) + "Require necessary extensions for DV, raise warnings for missing executables." (cl-loop with (m . h) = (cons dirvish-mode-line-format dirvish-header-line-format) - with (ml-l . ml-r) = (cons (plist-get m :left) (plist-get m :right)) - with (hl-l . hl-r) = (cons (plist-get h :left) (plist-get h :right)) - with feat-reqs = (append dirvish-preview-dispatchers ml-l ml-r hl-l hl-r) - with attrs = '(hl-line symlink-target) - for (lib . feat) in dirvish-libraries do - (let ((m-attr (cl-intersection feat dirvish-attributes)) - (feat-in-lib (cl-intersection feat feat-reqs))) - (when (or m-attr feat-in-lib) (require lib)) - (and m-attr (setq attrs (append attrs m-attr)))) - finally - (setf dirvish--mode-line-fmt (dirvish--mode-line-composer ml-l ml-r)) - (setf dirvish--header-line-fmt (dirvish--mode-line-composer hl-l hl-r t)) - (setf dirvish--working-preview-dispathchers (dirvish--preview-dps-validate)) - (setf dirvish--working-attrs (dirvish--attrs-expand attrs)))) + with (ml . mr) = (cons (plist-get m :left) (plist-get m :right)) + with (hl . hr) = (cons (plist-get h :left) (plist-get h :right)) + with attrs = (append '(hl-line symlink-target) dirvish-attributes) + with feat-reqs = (append dirvish-preview-dispatchers attrs ml mr hl hr) + when feat-reqs do (require 'dirvish-widgets) + for (lib . feat) in dirvish--libraries do + (when (cl-intersection feat feat-reqs) (require lib)) + finally (setf (dv-mode-line dv) (dirvish--mode-line-composer ml mr) + (dv-header-line dv) (dirvish--mode-line-composer hl hr t) + (dv-preview-dispatchers dv) (dirvish--preview-dps-validate) + (dv-attributes dv) (dirvish--attrs-expand attrs)))) (defun dirvish--only-index () "If `dired-kill-when-opening-new-dired-buffer', only keep session index." @@ -680,19 +673,6 @@ buffer, it defaults to filename under the cursor when it is nil." (let (dirvish-hide-cursor) (dirvish--maybe-toggle-cursor 'hollow)) (dirvish--render-attrs 'clear)) -(defun dirvish-thumb-buf-a (fn) - "Advice for FN `image-dired-create-thumbnail-buffer'." - (when-let* ((dv (dirvish-curr)) ((dv-preview-window dv))) - (dirvish--build-layout dv) - (with-selected-window (dv-preview-window dv) - (switch-to-buffer image-dired-thumbnail-buffer))) - (let ((buf (funcall fn)) - (fun (lambda () (let ((buf (get-text-property - (point) 'associated-dired-buffer))) - (and (buffer-live-p buf) - (with-current-buffer buf (dirvish--render-attrs))))))) - (with-current-buffer buf (add-hook 'post-command-hook fun nil t)) buf)) - (defun dirvish-dired-noselect-a (fn dir-or-list &optional flags) "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)) @@ -725,6 +705,9 @@ buffer, it defaults to filename under the cursor when it is nil." (dirvish-prop :gui (display-graphic-p)) (dirvish-prop :remote remote) (dirvish-prop :root key) + (dirvish-prop :preview-dps + (if remote '(dirvish-tramp-dp) (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)))) (when bname (dired-goto-file bname)) @@ -939,7 +922,9 @@ When PROC finishes, fill preview buffer with process result." ((window-live-p window)) (orig-bufs (buffer-list)) (ext (downcase (or (file-name-extension index) ""))) - (buf (cl-loop for fn in dirvish--working-preview-dispathchers + (fns (with-current-buffer (window-buffer (dv-root-window dv)) + (dirvish-prop :preview-dps))) + (buf (cl-loop for fn in fns for rcp = (funcall fn index ext window dv) thereis (and rcp (dirvish-preview-dispatch rcp dv))))) (setq-local other-window-scroll-buffer buf) @@ -1002,7 +987,7 @@ When PROC finishes, fill preview buffer with process result." with fns = () with height = (frame-height) with no-hl = (dirvish--apply-hiding-p dirvish-hide-cursor) with remain = (- (window-width) (if gui 1 2)) - for (_ width pred render ov) in dirvish--working-attrs + for (_ width pred render ov) in (dirvish-prop :attrs) do (remove-overlays (point-min) (point-max) ov t) when (eval pred `((win-width . ,remain))) do (setq remain (- remain width)) (push render fns) @@ -1035,9 +1020,8 @@ This attribute is disabled when cursor is visible." ;;;; Mode Line | Header Line (defun dirvish--mode-line-composer (left right &optional header) - "Set `dirvish--mode-line-fmt'. -LEFT and RIGHT are segments aligned to left/right respectively. -If HEADER, set the `dirvish--header-line-fmt' instead." + "Compose `mode-line-format' from LEFT and RIGHT segments. +If HEADER, the format is used for `header-line-format'." `((:eval (let* ((dv (dirvish-curr)) (fullframe-p (and dv (dv-curr-layout dv))) @@ -1093,8 +1077,8 @@ use `car'. If HEADER, use `dirvish-header-line-height' instead." (defun dirvish--setup-mode-line (dv) "Setup the mode/header line for dirvish DV." (let* ((idx-buf (cdr (dv-index dv))) - (hl (or (dirvish-prop :cus-header) dirvish--header-line-fmt)) - (ml dirvish--mode-line-fmt) + (hl (or (dirvish-prop :cus-header) (dv-header-line dv))) + (ml (dv-mode-line dv)) (fullframe-p (dv-curr-layout dv))) (cond ; setup `header-line-format' ((and fullframe-p (not dirvish-use-header-line))) @@ -1162,8 +1146,6 @@ Dirvish sets `revert-buffer-function' to this function." (defun dirvish-init-dired-buffer () "Initialize a Dired buffer for Dirvish." - (when (file-remote-p default-directory) - (setq-local dirvish--working-preview-dispathchers '(dirvish-tramp-dp))) (use-local-map dirvish-mode-map) (dirvish--hide-dired-header) (dirvish--maybe-toggle-cursor 'box) ; restore from `wdired' @@ -1208,8 +1190,8 @@ LEVEL is the depth of current window." (font-lock-mode 1) (dired-goto-file-1 (file-name-nondirectory index) index (point-max)) (dirvish--maybe-toggle-cursor '(box . 0)) ; always hide cursor in parents - (setq-local dirvish--attrs-hash (make-hash-table :test #'equal) - dirvish--working-attrs (dirvish--attrs-expand attrs)) + (dirvish-prop :attrs (dirvish--attrs-expand attrs)) + (setq-local dirvish--attrs-hash (make-hash-table :test #'equal)) (dirvish--render-attrs) buf))) (defun dirvish--create-parent-windows (dv) diff --git a/extensions/dirvish-side.el b/extensions/dirvish-side.el index f7271a591e..3528775e57 100644 --- a/extensions/dirvish-side.el +++ b/extensions/dirvish-side.el @@ -28,6 +28,21 @@ "Window parameters for `dirvish-side' window." :group 'dirvish :type 'alist) +(defcustom dirvish-side-mode-line-format dirvish-mode-line-format + "Mode line format used in `dirvish-side' window. +See `dirvish-mode-line-format' for details." + :group 'dirvish :type 'plist) + +(defcustom dirvish-side-header-line-format '(:left (project)) + "Header line format used in `dirvish-side' window. +See `dirvish-mode-line-format' for details." + :group 'dirvish :type 'plist) + +(defcustom dirvish-side-attributes dirvish-attributes + "File attributes used in `dirvish-side' window. +See `dirvish-attributes' for details." + :group 'dirvish :type '(repeat (symbol :tag "Dirvish attribute"))) + (defcustom dirvish-side-open-file-action 'mru "The action of how to open a file in side window. The value can be one of: @@ -53,8 +68,6 @@ If non-nil, expand all the parent directories of current buffer's filename until the project root when opening a side session." :group 'dirvish :type 'boolean) -(defconst dirvish-side-header (dirvish--mode-line-composer '(project) nil t)) - (defun dirvish-side-open-file-fn () "Called before opening a file in side sessions." (let* ((dv (dirvish-curr)) (layout (dv-curr-layout dv)) @@ -115,12 +128,14 @@ filename until the project root when opening a side session." (let (buffer-list-update-hook) (dirvish-find-entry-a dir)) (if dirvish-side-auto-expand (dirvish-subtree-expand-to curr) (dired-goto-file curr)) - (dirvish-prop :cus-header 'dirvish-side-header) (dirvish-update-body-h)))))) (defun dirvish-side--new (path) "Open a side session in PATH." (let* ((bname buffer-file-name) + (dirvish-mode-line-format dirvish-side-mode-line-format) + (dirvish-header-line-format dirvish-side-header-line-format) + (dirvish-attributes dirvish-side-attributes) (dv (or (dirvish--get-session 'type 'side) (dirvish--new :type 'side @@ -139,20 +154,8 @@ filename until the project root when opening a side session." (dirvish-side-auto-expand (dirvish-subtree-expand-to bname)) (t (dired-goto-file bname))) - (dirvish-prop :cus-header 'dirvish-side-header) (dirvish-update-body-h)))) -(dirvish-define-mode-line project - "Return a string showing current project." - (let ((project (dirvish--get-project-root)) - (face (if (dirvish--selected-p) 'dired-header 'dirvish-inactive))) - (if project - (setq project (file-name-base (directory-file-name project))) - (setq project "-")) - (format " %s %s" - (propertize "Project:" 'face face) - (propertize project 'face 'font-lock-string-face)))) - ;;;###autoload (define-minor-mode dirvish-side-follow-mode "Toggle `dirvish-side-follow-mode'. diff --git a/extensions/dirvish-vc.el b/extensions/dirvish-vc.el index c8b5160d5d..52dbfce97d 100644 --- a/extensions/dirvish-vc.el +++ b/extensions/dirvish-vc.el @@ -153,8 +153,7 @@ It is called when `:vc-backend' is included in DIRVISH-PROPs while (new-dps (seq-difference dirvish-preview-dispatchers '(vc-diff vc-log vc-blame)))) (when value (push (intern (format "%s" value)) new-dps)) - (setq-local dirvish--working-preview-dispathchers - (dirvish--preview-dps-validate new-dps)) + (dirvish-prop :preview-dps (dirvish--preview-dps-validate new-dps)) (if (not new-layout) (dirvish--preview-update dv (dirvish-prop :index)) (quit-window nil (dv-root-window dv))