diff options
Diffstat (limited to 'duet.el')
| -rw-r--r-- | duet.el | 133 |
1 files changed, 130 insertions, 3 deletions
@@ -721,11 +721,138 @@ ungated." (list :op 'delete :source s :gate 'copy-success))))) sources))) +;;; Commander mode, pane layout, and entry + +(defcustom duet-default-left-directory nil + "Directory shown in DUET's left pane at launch. +nil means the current directory when `duet' is invoked." + :type '(choice (const :tag "Current directory" nil) directory) + :group 'duet) + +(defcustom duet-default-right-directory nil + "Directory shown in DUET's right pane at launch. +nil means the current directory when `duet' is invoked." + :type '(choice (const :tag "Current directory" nil) directory) + :group 'duet) + +(defvar duet--saved-window-configuration nil + "Window configuration saved when DUET launched, restored by `duet-quit'.") + +;; The transfer/file actions land with their owning phases; until then they +;; announce themselves so the key is bound and its precedence is testable. + +(defun duet-view () + "View the file under point. Arrives with the viewer phase." + (interactive) + (user-error "DUET: view is not implemented yet")) + +(defun duet-edit () + "Edit the file under point. Arrives with the viewer phase." + (interactive) + (user-error "DUET: edit is not implemented yet")) + +(defun duet-copy () + "Copy the marked files to the other pane. Arrives with transfer execution." + (interactive) + (user-error "DUET: copy is not implemented yet")) + +(defun duet-move () + "Move the marked files to the other pane. Arrives with transfer execution." + (interactive) + (user-error "DUET: move is not implemented yet")) + +(defun duet-mkdir () + "Make a directory in the active pane. Arrives with transfer execution." + (interactive) + (user-error "DUET: mkdir is not implemented yet")) + +(defun duet-delete () + "Delete the marked files (to trash). Arrives with transfer execution." + (interactive) + (user-error "DUET: delete is not implemented yet")) + +(defun duet-quit () + "Close the DUET commander, restoring the window layout from before launch." + (interactive) + (if duet--saved-window-configuration + (progn + (set-window-configuration duet--saved-window-configuration) + (setq duet--saved-window-configuration nil)) + (message "DUET: no saved layout to restore"))) + +(defvar duet-mode-map + (let ((map (make-sparse-keymap))) + (define-key map (kbd "<f3>") #'duet-view) + (define-key map (kbd "<f4>") #'duet-edit) + (define-key map (kbd "<f5>") #'duet-copy) + (define-key map (kbd "<f6>") #'duet-move) + (define-key map (kbd "<f7>") #'duet-mkdir) + (define-key map (kbd "<f8>") #'duet-delete) + (define-key map (kbd "<f10>") #'duet-quit) + map) + "Keymap active in DUET commander panes. +mc/Norton F-keys, taking precedence inside a pane only. dired's own chords +\(C, R, D, v, +) keep working as free aliases.") + +(define-minor-mode duet-mode + "Buffer-local minor mode marking a buffer as a DUET commander pane. +Carries `duet-mode-map', so the single-key F-key actions fire only inside a +commander pane and leave the global F-keys untouched elsewhere." + :lighter " Duet" + :keymap duet-mode-map) + +(defun duet--sibling-pane (window panes) + "Return the commander pane in PANES that is not WINDOW. +PANES is the list of commander windows. Signal a `user-error' unless there +are exactly two panes and WINDOW is one of them. This is the explicit +other-pane resolution that replaces `dired-dwim-target' (the dirvish#36 fix)." + (cond + ((/= (length panes) 2) + (user-error "DUET needs exactly two commander panes (found %d)" + (length panes))) + ((not (memq window panes)) + (user-error "Point is not in a DUET commander pane")) + (t (car (delq window (copy-sequence panes)))))) + +(defun duet--commander-windows () + "Return the live windows whose buffer has `duet-mode' enabled." + (cl-remove-if-not (lambda (w) (buffer-local-value 'duet-mode (window-buffer w))) + (window-list))) + +(defun duet--other-pane (&optional window) + "Return the sibling commander pane window to WINDOW (default selected)." + (duet--sibling-pane (or window (selected-window)) (duet--commander-windows))) + +(defun duet--pane-directory (window) + "Return the directory shown in commander pane WINDOW." + (with-current-buffer (window-buffer window) default-directory)) + +(defun duet--open-pane (directory) + "Open DIRECTORY in the selected window as a DUET commander pane. +The pane is a dired buffer with `duet-mode' enabled; when the user has dirvish +rendering dired buffers, it renders as dirvish with no extra work, which is why +dirvish is recommended but never required." + (let ((buffer (dired-noselect (expand-file-name directory)))) + (set-window-buffer (selected-window) buffer) + (with-current-buffer buffer (duet-mode 1)) + buffer)) + ;;;###autoload -(defun duet () - "Launch the DUET dual-pane file commander." +(defun duet (&optional left right) + "Launch the DUET dual-pane file commander. +Lay out two side-by-side commander panes: LEFT (or +`duet-default-left-directory', else the current directory) on the left and +RIGHT (or `duet-default-right-directory', else the current directory) on the +right. Each pane is a dired buffer with `duet-mode' enabled. `duet-quit' +\(F10) restores the window layout from before launch." (interactive) - (user-error "DUET is not yet implemented; see the design document")) + (setq duet--saved-window-configuration (current-window-configuration)) + (let ((left-dir (or left duet-default-left-directory default-directory)) + (right-dir (or right duet-default-right-directory default-directory))) + (delete-other-windows) + (duet--open-pane left-dir) + (with-selected-window (split-window-right) + (duet--open-pane right-dir)))) (provide 'duet) ;;; duet.el ends here |
