;;; +eshell.el -*- lexical-binding: t; -*- ;;; Code: ;; https://karthinks.com/software/jumping-directories-in-eshell/ (defun eshell/z (&optional regexp) "Navigate to a previously visited directory in eshell, or to any directory proferred by `consult-dir'." (let ((eshell-dirs (delete-dups (mapcar 'abbreviate-file-name (ring-elements eshell-last-dir-ring))))) (cond ((and (not regexp) (featurep 'consult-dir)) (let* ((consult-dir--source-eshell `(:name "Eshell" :narrow ?e :category file :face consult-file :items ,eshell-dirs)) (consult-dir-sources (cons consult-dir--source-eshell consult-dir-sources))) (eshell/cd (substring-no-properties (consult-dir--pick "Switch directory: "))))) (t (eshell/cd (if regexp (eshell-find-previous-directory regexp) (completing-read "cd: " eshell-dirs))))))) ;;; Start and quit ;; from https://old.reddit.com/r/emacs/comments/1zkj2d/advanced_usage_of_eshell/ (defun +eshell-here () "Go to eshell and set current directory to current buffer's." ;; consider: make a new eshell buffer when given a prefix argument. (interactive) (let ((dir (file-name-directory (or (buffer-file-name) default-directory)))) (eshell) (eshell/pushd ".") (cd dir) (goto-char (point-max)) (eshell-kill-input) (eshell-send-input) (setq-local scroll-margin 0) (recenter 0))) (defun +eshell-quit-or-delete-char (arg) "Delete the character to the right, or quit eshell on an empty line." (interactive "p") (if (and (eolp) (looking-back eshell-prompt-regexp)) (progn (eshell-life-is-too-much) (when (and (<= 1 (count-windows)) ;; TODO: This is not what I want. What I really want is ;; for an eshell-only frame (i.e., called from a ;; keybind) to delete itself, but a regular Emacs frame ;; with Eshell inside to stick around. I think I'll ;; need to make a frame-local (?) variable for that to ;; work. (> (length (frame-list)) 2) server-process) (delete-frame))) (delete-forward-char arg))) ;;; Insert previous arguments ;; Record arguments (defvar eshell-arg-history nil) (defvar eshell-arg-history-index nil) (add-to-list 'savehist-additional-variables 'eshell-arg-history) (defun eshell-record-args (&rest _) "Record unique arguments onto the front of `eshell-arg-history'." (setq eshell-arg-history (cl-loop with history = eshell-arg-history for arg in (reverse eshell-last-arguments) do (setq history (cons arg (remove arg history))) finally return history))) (defun eshell-insert-prev-arg () "Insert an argument from `eshell-arg-history' at point." (interactive) (if (eq last-command 'eshell-insert-prev-arg) (progn (let ((pos (point))) (eshell-backward-argument 1) (delete-region (point) pos)) (if-let ((text (nth eshell-arg-history-index eshell-arg-history))) (progn (insert text) (cl-incf eshell-arg-history-index)) (insert (cl-first eshell-arg-history)) (setq eshell-arg-history-index 1))) (insert (cl-first eshell-arg-history)) (setq eshell-arg-history-index 1))) ;;;###autoload (define-minor-mode eshell-arg-hist-mode "Minor mode to enable argument history, like bash/zsh with M-." :lighter "$." :keymap (let ((map (make-sparse-keymap))) (define-key map (kbd "M-.") #'eshell-insert-prev-arg) map) (if eshell-arg-hist-mode (add-hook 'eshell-post-command-hook #'eshell-record-args nil t) (remove-hook 'eshell-post-command-hook #'eshell-record-args t))) ;;;###autoload (defmacro +eshell-eval-after-load (&rest forms) "Execute FORMS after Eshell is loaded. If Eshell is already loaded in the session, immediately execute forms. I wrote this because Eshell doesn't properly do loading or something, it's really annoying to work with." (declare (indent 0)) `(progn (defun +eshell@setup () "Setup the Eshell session." ,@forms) (when (featurep 'eshell) `(dolist (buf (buffer-list)) (with-current-buffer buf (when (derived-mode-p 'eshell-mode) (+eshell@setup))))) (add-hook 'eshell-mode-hook #'+eshell@setup))) (provide '+eshell) ;;; +eshell.el ends here