emacs/lisp/+eshell.el

127 lines
4.8 KiB
EmacsLisp

;;; +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