2019-05-18 23:41:18 -04:00
|
|
|
;;; term/eshell/autoload/eshell.el -*- lexical-binding: t; -*-
|
2017-02-19 18:53:38 -05:00
|
|
|
|
2019-05-15 12:30:41 -04:00
|
|
|
(defvar eshell-buffer-name "*doom:eshell*")
|
2018-03-24 07:36:59 -04:00
|
|
|
|
|
|
|
(defvar +eshell-buffers (make-ring 25)
|
2017-09-27 12:42:24 +02:00
|
|
|
"List of open eshell buffers.")
|
|
|
|
|
2017-02-19 18:53:38 -05:00
|
|
|
|
2018-06-18 22:31:27 +02:00
|
|
|
(defvar +eshell--last-buffer nil)
|
2018-03-24 07:36:59 -04:00
|
|
|
|
|
|
|
|
|
|
|
;;
|
2018-06-18 22:31:27 +02:00
|
|
|
;; Helpers
|
2018-03-24 07:36:59 -04:00
|
|
|
|
2018-03-24 17:51:55 -04:00
|
|
|
(defun +eshell--add-buffer (buf)
|
2018-07-11 00:02:48 +02:00
|
|
|
(ring-remove+insert+extend +eshell-buffers buf 'grow))
|
2018-03-24 17:51:55 -04:00
|
|
|
|
|
|
|
(defun +eshell--remove-buffer (buf)
|
|
|
|
(when-let* ((idx (ring-member +eshell-buffers buf)))
|
2018-05-18 01:22:49 +02:00
|
|
|
(ring-remove +eshell-buffers idx)
|
|
|
|
t))
|
2018-03-24 17:51:55 -04:00
|
|
|
|
2018-06-28 18:03:45 +02:00
|
|
|
(defun +eshell--bury-buffer (&optional dedicated-p)
|
2018-06-18 22:31:27 +02:00
|
|
|
(unless (switch-to-prev-buffer nil 'bury)
|
2018-06-27 19:34:48 +02:00
|
|
|
(switch-to-buffer (doom-fallback-buffer)))
|
2018-06-30 02:12:09 +02:00
|
|
|
(when (eq major-mode 'eshell-mode)
|
|
|
|
(switch-to-buffer (doom-fallback-buffer)))
|
2018-06-27 19:34:48 +02:00
|
|
|
(when +eshell-enable-new-shell-on-split
|
2018-06-28 18:03:45 +02:00
|
|
|
(when-let* ((win (get-buffer-window (+eshell/open t))))
|
|
|
|
(set-window-dedicated-p win dedicated-p))))
|
2018-06-18 22:31:27 +02:00
|
|
|
|
|
|
|
(defun +eshell--setup-window (window &optional flag)
|
|
|
|
(when (window-live-p window)
|
|
|
|
(set-window-parameter window 'no-other-window flag)
|
|
|
|
(set-window-parameter window 'visible flag)))
|
2018-03-24 07:36:59 -04:00
|
|
|
|
2018-06-18 22:31:27 +02:00
|
|
|
(defun +eshell--unused-buffer (&optional new-p)
|
2018-03-24 23:19:14 -04:00
|
|
|
(or (unless new-p
|
2018-07-11 00:02:48 +02:00
|
|
|
(cl-loop for buf in (+eshell-buffers)
|
2018-03-24 23:19:14 -04:00
|
|
|
if (and (buffer-live-p buf)
|
2018-06-18 22:31:27 +02:00
|
|
|
(not (get-buffer-window buf t)))
|
2018-03-24 23:19:14 -04:00
|
|
|
return buf))
|
2018-06-18 22:31:27 +02:00
|
|
|
(generate-new-buffer eshell-buffer-name)))
|
2018-03-24 07:36:59 -04:00
|
|
|
|
2018-06-18 22:31:27 +02:00
|
|
|
;;;###autoload
|
|
|
|
(defun +eshell-last-buffer (&optional noerror)
|
|
|
|
"Return the last opened eshell buffer."
|
2018-07-11 00:02:48 +02:00
|
|
|
(let ((buffer (cl-find-if #'buffer-live-p (+eshell-buffers))))
|
|
|
|
(cond (buffer)
|
2018-06-18 22:31:27 +02:00
|
|
|
(noerror nil)
|
|
|
|
((user-error "No live eshell buffers remaining")))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +eshell-buffers ()
|
|
|
|
"TODO"
|
|
|
|
(ring-elements +eshell-buffers))
|
2018-05-18 01:22:49 +02:00
|
|
|
|
2018-03-24 17:51:55 -04:00
|
|
|
;;;###autoload
|
2018-06-18 22:31:27 +02:00
|
|
|
(defun +eshell-run-command (command &optional buffer)
|
|
|
|
"TODO"
|
|
|
|
(let ((buffer
|
|
|
|
(or buffer
|
|
|
|
(if (eq major-mode 'eshell-mode)
|
|
|
|
(current-buffer)
|
2018-07-11 00:02:48 +02:00
|
|
|
(cl-find-if #'buffer-live-p (+eshell-buffers))))))
|
2018-06-18 22:31:27 +02:00
|
|
|
(unless buffer
|
|
|
|
(user-error "No living eshell buffers available"))
|
|
|
|
(unless (buffer-live-p buffer)
|
|
|
|
(user-error "Cannot operate on a dead buffer"))
|
|
|
|
(with-current-buffer buffer
|
|
|
|
(goto-char eshell-last-output-end)
|
|
|
|
(goto-char (line-end-position))
|
|
|
|
(insert command)
|
|
|
|
(eshell-send-input nil t))))
|
2018-03-24 17:51:55 -04:00
|
|
|
|
2017-09-27 14:48:05 +02:00
|
|
|
|
2018-03-24 07:36:59 -04:00
|
|
|
;;
|
|
|
|
;; Commands
|
2017-09-27 14:48:05 +02:00
|
|
|
|
2017-02-19 18:53:38 -05:00
|
|
|
;;;###autoload
|
2018-06-18 14:42:42 +02:00
|
|
|
(defun +eshell/open (arg &optional command)
|
2017-02-19 18:53:38 -05:00
|
|
|
"Open eshell in the current buffer."
|
2018-06-18 14:42:42 +02:00
|
|
|
(interactive "P")
|
2018-06-18 22:31:27 +02:00
|
|
|
(when (eq major-mode 'eshell-mode)
|
|
|
|
(user-error "Already in an eshell buffer"))
|
2018-10-01 12:06:08 -04:00
|
|
|
(let* ((default-directory (or (if arg default-directory (doom-project-root))
|
|
|
|
default-directory))
|
2018-06-18 22:31:27 +02:00
|
|
|
(buf (+eshell--unused-buffer)))
|
|
|
|
(with-current-buffer (switch-to-buffer buf)
|
2018-06-28 14:25:03 +02:00
|
|
|
(if (eq major-mode 'eshell-mode)
|
|
|
|
(run-hooks 'eshell-mode-hook)
|
|
|
|
(eshell-mode))
|
2018-06-18 22:31:27 +02:00
|
|
|
(if command (+eshell-run-command command buf)))
|
|
|
|
buf))
|
2017-02-19 18:53:38 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
2018-06-18 14:42:42 +02:00
|
|
|
(defun +eshell/open-popup (arg &optional command)
|
2017-02-19 18:53:38 -05:00
|
|
|
"Open eshell in a popup window."
|
2018-06-18 14:42:42 +02:00
|
|
|
(interactive "P")
|
2018-10-01 12:06:08 -04:00
|
|
|
(let* ((default-directory (or (if arg default-directory (doom-project-root))
|
|
|
|
default-directory))
|
2018-06-18 22:31:27 +02:00
|
|
|
(buf (+eshell--unused-buffer)))
|
|
|
|
(with-current-buffer (pop-to-buffer buf)
|
2018-06-28 14:25:03 +02:00
|
|
|
(if (eq major-mode 'eshell-mode)
|
|
|
|
(run-hooks 'eshell-mode-hook)
|
|
|
|
(eshell-mode))
|
2018-06-18 22:31:27 +02:00
|
|
|
(if command (+eshell-run-command command buf)))
|
|
|
|
buf))
|
2017-02-19 18:53:38 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
2018-06-18 22:31:27 +02:00
|
|
|
(defun +eshell/open-fullscreen (arg &optional command)
|
2019-04-21 19:59:44 -04:00
|
|
|
"Open eshell in a separate workspace. Requires the (:ui workspaces)
|
2017-02-19 18:53:38 -05:00
|
|
|
module to be loaded."
|
2018-06-18 14:42:42 +02:00
|
|
|
(interactive "P")
|
2018-10-01 12:06:08 -04:00
|
|
|
(let ((default-directory (or (if arg default-directory (doom-project-root))
|
|
|
|
default-directory))
|
2018-06-27 19:23:27 +02:00
|
|
|
(buf (+eshell--unused-buffer 'new)))
|
|
|
|
(set-frame-parameter nil 'saved-wconf (current-window-configuration))
|
2018-06-18 22:31:27 +02:00
|
|
|
(delete-other-windows)
|
2018-06-27 19:23:27 +02:00
|
|
|
(with-current-buffer (switch-to-buffer buf)
|
|
|
|
(eshell-mode)
|
|
|
|
(if command (+eshell-run-command command buf)))
|
|
|
|
buf))
|
2018-06-16 16:37:43 +02:00
|
|
|
|
2017-09-27 12:42:24 +02:00
|
|
|
|
2018-03-24 07:36:59 -04:00
|
|
|
;;
|
2018-06-18 22:31:27 +02:00
|
|
|
;; Keybinds
|
2017-09-27 12:42:24 +02:00
|
|
|
|
|
|
|
;;;###autoload
|
2018-06-18 22:31:27 +02:00
|
|
|
(defun +eshell/search-history ()
|
|
|
|
"Search the eshell command history with helm, ivy or `eshell-list-history'."
|
|
|
|
(interactive)
|
|
|
|
(cond ((featurep! :completion ivy)
|
|
|
|
(require 'em-hist)
|
|
|
|
(let* ((ivy-completion-beg (eshell-bol))
|
|
|
|
(ivy-completion-end (point-at-eol))
|
|
|
|
(input (buffer-substring-no-properties
|
|
|
|
ivy-completion-beg
|
|
|
|
ivy-completion-end)))
|
|
|
|
;; Better than `counsel-esh-history' because that doesn't
|
|
|
|
;; pre-populate the initial input or selection.
|
|
|
|
(ivy-read "Command: "
|
|
|
|
(delete-dups
|
|
|
|
(when (> (ring-size eshell-history-ring) 0)
|
|
|
|
(ring-elements eshell-history-ring)))
|
|
|
|
:initial-input input
|
|
|
|
:action #'ivy-completion-in-region-action)))
|
|
|
|
((featurep! :completion helm)
|
|
|
|
(helm-eshell-history))
|
|
|
|
((eshell-list-history))))
|
2017-09-27 12:42:24 +02:00
|
|
|
|
|
|
|
;;;###autoload
|
2018-06-18 22:31:27 +02:00
|
|
|
(defun +eshell/pcomplete ()
|
|
|
|
"Use pcomplete with completion-in-region backend instead of popup window at
|
|
|
|
bottom. This ties pcomplete into ivy or helm, if they are enabled."
|
|
|
|
(interactive)
|
|
|
|
(require 'pcomplete)
|
2018-06-30 02:21:02 +02:00
|
|
|
(ignore-errors (pcomplete-std-complete)))
|
2017-09-27 12:42:24 +02:00
|
|
|
|
2017-07-21 16:50:44 +02:00
|
|
|
;;;###autoload
|
|
|
|
(defun +eshell/quit-or-delete-char (arg)
|
2018-03-24 07:36:59 -04:00
|
|
|
"Delete a character (ahead of the cursor) or quit eshell if there's nothing to
|
|
|
|
delete."
|
2017-07-21 16:50:44 +02:00
|
|
|
(interactive "p")
|
2017-07-22 00:12:04 +02:00
|
|
|
(if (and (eolp) (looking-back eshell-prompt-regexp nil))
|
2017-07-21 16:50:44 +02:00
|
|
|
(eshell-life-is-too-much)
|
2017-07-22 00:12:04 +02:00
|
|
|
(delete-char arg)))
|
2017-07-21 16:50:44 +02:00
|
|
|
|
2017-02-19 18:53:38 -05:00
|
|
|
;;;###autoload
|
2018-03-24 17:51:55 -04:00
|
|
|
(defun +eshell/split-below ()
|
|
|
|
"Create a new eshell window below the current one."
|
2017-02-19 18:53:38 -05:00
|
|
|
(interactive)
|
2018-06-27 19:34:48 +02:00
|
|
|
(let ((ignore-window-parameters t)
|
2018-06-28 18:03:45 +02:00
|
|
|
(dedicated-p (window-dedicated-p))
|
2018-06-27 19:34:48 +02:00
|
|
|
(+eshell-enable-new-shell-on-split
|
|
|
|
(or +eshell-enable-new-shell-on-split (frame-parameter nil 'saved-wconf))))
|
|
|
|
(select-window (split-window-vertically))
|
2018-06-28 18:03:45 +02:00
|
|
|
(+eshell--bury-buffer dedicated-p)))
|
2017-02-19 18:53:38 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
2018-03-24 17:51:55 -04:00
|
|
|
(defun +eshell/split-right ()
|
|
|
|
"Create a new eshell window to the right of the current one."
|
2017-02-19 18:53:38 -05:00
|
|
|
(interactive)
|
2018-06-27 19:34:48 +02:00
|
|
|
(let* ((ignore-window-parameters t)
|
2018-06-28 18:03:45 +02:00
|
|
|
(dedicated-p (window-dedicated-p))
|
2018-06-27 19:34:48 +02:00
|
|
|
(+eshell-enable-new-shell-on-split
|
|
|
|
(or +eshell-enable-new-shell-on-split (frame-parameter nil 'saved-wconf))))
|
|
|
|
(select-window (split-window-horizontally))
|
2018-06-28 18:03:45 +02:00
|
|
|
(+eshell--bury-buffer dedicated-p)))
|
2017-02-19 18:53:38 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
2018-06-18 22:31:27 +02:00
|
|
|
(defun +eshell/switch-to-next ()
|
2018-03-24 07:36:59 -04:00
|
|
|
"Switch to the next eshell buffer."
|
|
|
|
(interactive)
|
|
|
|
(when (ring-empty-p +eshell-buffers)
|
|
|
|
(user-error "No eshell buffers are available"))
|
|
|
|
(switch-to-buffer (ring-next +eshell-buffers (current-buffer))))
|
|
|
|
|
|
|
|
;;;###autoload
|
2018-06-18 22:31:27 +02:00
|
|
|
(defun +eshell/switch-to-previous ()
|
2018-03-24 07:36:59 -04:00
|
|
|
"Switch to the previous eshell buffer."
|
|
|
|
(interactive)
|
|
|
|
(when (ring-empty-p +eshell-buffers)
|
|
|
|
(user-error "No eshell buffers are available"))
|
|
|
|
(switch-to-buffer (ring-previous +eshell-buffers (current-buffer))))
|
2017-02-19 18:53:38 -05:00
|
|
|
|
2018-03-24 07:36:59 -04:00
|
|
|
;;;###autoload
|
2018-06-18 22:31:27 +02:00
|
|
|
(defun +eshell/switch-to-last ()
|
2018-03-24 07:36:59 -04:00
|
|
|
"Switch to the last eshell buffer that was open (and is still alive)."
|
|
|
|
(interactive)
|
2018-06-18 22:31:27 +02:00
|
|
|
(unless (buffer-live-p +eshell--last-buffer)
|
|
|
|
(setq +eshell--last-buffer nil)
|
2018-03-24 07:36:59 -04:00
|
|
|
(user-error "No last eshell buffer to jump to"))
|
2018-06-18 22:31:27 +02:00
|
|
|
(switch-to-buffer +eshell--last-buffer))
|
2018-03-24 07:36:59 -04:00
|
|
|
|
|
|
|
;;;###autoload
|
2018-06-18 22:31:27 +02:00
|
|
|
(defun +eshell/switch-to (buffer)
|
2018-03-24 07:36:59 -04:00
|
|
|
"Interactively switch to another eshell buffer."
|
|
|
|
(interactive
|
2018-06-17 02:05:33 +02:00
|
|
|
(let ((buffers (doom-buffers-in-mode
|
2018-07-11 00:02:48 +02:00
|
|
|
'eshell-mode (delq (current-buffer) (+eshell-buffers)))))
|
2018-03-28 19:07:48 -04:00
|
|
|
(if (not buffers)
|
2018-03-28 17:32:35 -04:00
|
|
|
(user-error "No eshell buffers are available")
|
2018-06-18 22:31:27 +02:00
|
|
|
(list
|
|
|
|
(completing-read "Eshell buffers"
|
|
|
|
(mapcar #'buffer-name buffers)
|
|
|
|
#'get-buffer
|
|
|
|
'require-match
|
|
|
|
nil nil
|
|
|
|
(when (eq major-mode 'eshell-mode)
|
|
|
|
(buffer-name (current-buffer))))))))
|
2018-03-28 19:07:48 -04:00
|
|
|
(if-let* ((window (get-buffer-window buffer)))
|
|
|
|
(select-window window)
|
|
|
|
(switch-to-buffer buffer)))
|
2018-06-18 22:31:27 +02:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +eshell/kill-and-close ()
|
|
|
|
"Kill the current eshell buffer and close its window."
|
|
|
|
(interactive)
|
|
|
|
(unless (eq major-mode 'eshell-mode)
|
|
|
|
(user-error "Not in an eshell buffer"))
|
|
|
|
(let ((+eshell-kill-window-on-exit t))
|
2019-05-30 18:54:58 -07:00
|
|
|
(kill-current-buffer)))
|
2018-06-18 22:31:27 +02:00
|
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
;; Hooks
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +eshell|init ()
|
|
|
|
"Initialize and track this eshell buffer in `+eshell-buffers'."
|
2018-07-11 00:02:48 +02:00
|
|
|
(let ((current-buffer (current-buffer)))
|
|
|
|
(dolist (buf (+eshell-buffers))
|
2018-06-18 22:31:27 +02:00
|
|
|
(unless (buffer-live-p buf)
|
|
|
|
(+eshell--remove-buffer buf)))
|
2018-07-11 00:02:48 +02:00
|
|
|
(+eshell--setup-window (get-buffer-window current-buffer))
|
|
|
|
(+eshell--add-buffer current-buffer)
|
|
|
|
(setq +eshell--last-buffer current-buffer)))
|
2018-06-18 22:31:27 +02:00
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +eshell|cleanup ()
|
|
|
|
"Close window (or workspace) on quit."
|
|
|
|
(let ((buf (current-buffer)))
|
|
|
|
(when (+eshell--remove-buffer buf)
|
2018-06-28 14:47:14 +02:00
|
|
|
(when-let* ((win (get-buffer-window buf)))
|
|
|
|
(+eshell--setup-window win nil)
|
|
|
|
(cond ((and (one-window-p t)
|
|
|
|
(window-configuration-p (frame-parameter nil 'saved-wconf)))
|
|
|
|
(set-window-configuration (frame-parameter nil 'saved-wconf))
|
|
|
|
(set-frame-parameter win 'saved-wconf nil))
|
|
|
|
((one-window-p)
|
|
|
|
(let ((prev (save-window-excursion (previous-buffer))))
|
|
|
|
(unless (and prev (doom-real-buffer-p prev))
|
|
|
|
(switch-to-buffer (doom-fallback-buffer)))))
|
2018-06-28 18:03:45 +02:00
|
|
|
((or (window-dedicated-p win)
|
2018-06-28 14:47:14 +02:00
|
|
|
+eshell-kill-window-on-exit)
|
2018-06-28 18:03:45 +02:00
|
|
|
(let ((ignore-window-parameters t)
|
|
|
|
(popup-p (window-dedicated-p win)))
|
|
|
|
(delete-window win)
|
|
|
|
(when popup-p
|
|
|
|
(cl-loop for win in (window-list)
|
|
|
|
for buf = (window-buffer win)
|
|
|
|
for mode = (buffer-local-value 'major-mode buf)
|
|
|
|
if (eq mode 'eshell-mode)
|
|
|
|
return (select-window win))))))))))
|
2018-06-18 22:31:27 +02:00
|
|
|
|
2018-07-11 12:42:33 +02:00
|
|
|
;;;###autoload
|
|
|
|
(defun +eshell|switch-workspace (type)
|
|
|
|
(when (eq type 'frame)
|
|
|
|
(setq +eshell-buffers
|
|
|
|
(or (persp-parameter 'eshell-buffers)
|
|
|
|
(make-ring 25)))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun +eshell|save-workspace (_workspace target)
|
|
|
|
(when (framep target)
|
|
|
|
(set-persp-parameter 'eshell-buffers +eshell-buffers)))
|