From a6cc5a219b8888a90143ea76fdd1ed153d04e655 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Sat, 24 Mar 2018 07:36:59 -0400 Subject: [PATCH] tools/eshell: major refactor; add eshell management commands --- modules/tools/eshell/autoload/eshell.el | 154 +++++++++++++++++++----- modules/tools/eshell/config.el | 6 +- 2 files changed, 131 insertions(+), 29 deletions(-) diff --git a/modules/tools/eshell/autoload/eshell.el b/modules/tools/eshell/autoload/eshell.el index 2cfe67e5a..c37316df7 100644 --- a/modules/tools/eshell/autoload/eshell.el +++ b/modules/tools/eshell/autoload/eshell.el @@ -1,20 +1,75 @@ ;;; tools/eshell/autoload/eshell.el -*- lexical-binding: t; -*- -(defvar +eshell-buffers () +(defface +eshell-prompt-pwd '((t :inherit eshell-prompt)) + "TODO") + +(defface +eshell-prompt-git-branch '((t :inherit font-lock-function-name-face)) + "TODO") + +(defface +eshell-prompt-char '((t :inherit font-lock-constant-face)) + "TODO") + + +(defvar +eshell-buffers (make-ring 25) "List of open eshell buffers.") (defvar +eshell-buffer-name "*doom eshell*" "The name to use for custom eshell buffers. This only affects `+eshell/open', `+eshell/open-popup' and `+eshell/open-workspace'.") +(defvar +eshell-last-buffer nil + "TODO") -;; --- Commands --------------------------- + +;; +;; Library +;; + +(defun +eshell--add-buffer (buf) + (ring-remove+insert+extend +eshell-buffers buf)) + +(defun +eshell--remove-buffer (buf) + (when-let* ((idx (ring-member +eshell-buffers buf))) + (ring-remove +eshell-buffers idx))) + +(defun +eshell--current-git-branch () + (let ((branch (car (cl-loop for match in (split-string (shell-command-to-string "git branch") "\n") + if (string-match-p "^\*" match) + collect match)))) + (if (not (eq branch nil)) + (format " [%s]" (substring branch 2)) + ""))) + +(defun +eshell-delete-window () + (if (one-window-p) + (unless (doom-real-buffer-p (progn (previous-buffer) (current-buffer))) + (switch-to-buffer (doom-fallback-buffer))) + (delete-window))) + +(defun +eshell-get-or-create-buffer () + (or (cl-loop for buf in (ring-elements +eshell-buffers) + if (and (buffer-live-p buf) + (not (get-buffer-window buf))) + return buf) + (generate-new-buffer +eshell-buffer-name))) + +;;;###autoload +(defun +eshell-prompt () + "Generate the prompt string for eshell. Use for `eshell-prompt-function'." + (concat (propertize (abbreviate-file-name (eshell/pwd)) 'face '+eshell-prompt-pwd) + (propertize (+eshell--current-git-branch) 'face '+eshell-prompt-git-branch) + (propertize " λ " 'face '+eshell-prompt-char))) + + +;; +;; Commands +;; ;;;###autoload (defun +eshell/open (&optional command) "Open eshell in the current buffer." (interactive) - (let ((buf (generate-new-buffer +eshell-buffer-name))) + (let ((buf (+eshell-get-or-create-buffer))) (with-current-buffer buf (unless (eq major-mode 'eshell-mode) (eshell-mode))) (switch-to-buffer buf) @@ -25,7 +80,7 @@ (defun +eshell/open-popup (&optional command) "Open eshell in a popup window." (interactive) - (let ((buf (get-buffer-create +eshell-buffer-name))) + (let ((buf (+eshell-get-or-create-buffer))) (with-current-buffer buf (unless (eq major-mode 'eshell-mode) (eshell-mode))) (pop-to-buffer buf) @@ -41,13 +96,14 @@ module to be loaded." (user-error ":feature workspaces is required, but disabled")) (unless (+workspace-get "eshell" t) (+workspace/new "eshell")) - (if-let* ((buf (cl-find-if (lambda (it) (string-match-p "^\\*doom:eshell" (buffer-name (window-buffer it)))) - (doom-visible-windows)))) + (if-let* ((buf (cl-find-if (lambda (buf) (eq 'eshell-mode (buffer-local-value 'major-mode buf))) + (doom-visible-windows) + :key #'window-buffer))) (select-window (get-buffer-window buf)) (+eshell/open)) - (doom/workspace-display) (when command - (+eshell-run-command command))) + (+eshell-run-command command)) + (doom/workspace-display)) (defun +eshell-run-command (command) (unless (cl-remove-if-not #'buffer-live-p +eshell-buffers) @@ -60,54 +116,98 @@ module to be loaded." (eshell-send-input nil t))) -;; --- Hooks ------------------------------ +;; +;; Hooks +;; ;;;###autoload (defun +eshell|init () "Keep track of eshell buffers." - (cl-pushnew (current-buffer) +eshell-buffers :test #'eq)) + (let ((buf (current-buffer))) + (remove-hook 'kill-buffer-query-functions #'doom|protect-visible-buffers t) + (add-hook 'kill-buffer-hook #'+eshell-delete-window nil t) + (dolist (buf (ring-elements +eshell-buffers)) + (unless (buffer-live-p buf) + (+eshell--remove-buffer buf))) + (+eshell--add-buffer buf) + (setq +eshell-last-buffer buf))) ;;;###autoload (defun +eshell|cleanup () "Close window (or workspace) on quit." - (setq +eshell-buffers (delete (current-buffer) +eshell-buffers)) + (+eshell--remove-buffer (current-buffer)) (when (and (featurep! :feature workspaces) (string= "eshell" (+workspace-current-name))) (+workspace/delete "eshell"))) -;; --- Keybindings ------------------------ +;; +;; Keybinds +;; ;;;###autoload (defun +eshell/quit-or-delete-char (arg) + "Delete a character (ahead of the cursor) or quit eshell if there's nothing to +delete." (interactive "p") (if (and (eolp) (looking-back eshell-prompt-regexp nil)) (eshell-life-is-too-much) (delete-char arg))) -(defun +eshell--current-git-branch () - (let ((branch (car (cl-loop for match in (split-string (shell-command-to-string "git branch") "\n") - if (string-match-p "^\*" match) - collect match)))) - (if (not (eq branch nil)) - (concat " [" (substring branch 2) "]") - ""))) - ;;;###autoload -(defun +eshell/split () +(defun +eshell/split-below () + "Create a new eshell window below the current one." (interactive) (select-window (split-window-vertically)) (+eshell/open)) ;;;###autoload -(defun +eshell/vsplit () +(defun +eshell/split-right () + "Create a new eshell window to the right of the current one." (interactive) (select-window (split-window-horizontally)) (+eshell/open)) -;;;###autoload -(defun +eshell-prompt () - (concat (propertize (abbreviate-file-name (eshell/pwd)) 'face 'eshell-prompt) - (propertize (+eshell--current-git-branch) 'face 'font-lock-function-name-face) - (propertize " λ " 'face 'font-lock-constant-face))) +;; `make-ring' +;; `ring-ref' +;; `ring-empty-p' +;; `ring-remove' +;;;###autoload +(defun +eshell/next () + "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 +(defun +eshell/previous () + "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)))) + +;;;###autoload +(defun +eshell/open-last () + "Switch to the last eshell buffer that was open (and is still alive)." + (interactive) + (unless (buffer-live-p +eshell-last-buffer) + (setq +eshell-last-buffer nil) + (user-error "No last eshell buffer to jump to")) + (switch-to-buffer +eshell-last-buffer)) + +;;;###autoload +(defun +eshell/switch (buffer) + "Interactively switch to another eshell buffer." + (interactive + (if (ring-empty-p +eshell-buffers) + (user-error "No eshell buffers are available") + (list (completing-read + "Eshell buffers" + (mapc #'buffer-name (delete (current-buffer) (ring-elements +eshell-buffers))) + #'get-buffer + 'require-match + nil nil (buffer-name (current-buffer)))))) + (switch-to-buffer buffer)) diff --git a/modules/tools/eshell/config.el b/modules/tools/eshell/config.el index 529e29317..cf2e5e729 100644 --- a/modules/tools/eshell/config.el +++ b/modules/tools/eshell/config.el @@ -61,7 +61,9 @@ redefines its keys every time `eshell-mode' is enabled." :i "C-n" #'eshell-next-input [remap doom/backward-to-bol-or-indent] #'eshell-bol [remap doom/backward-kill-to-bol-and-indent] #'eshell-kill-input - [remap evil-window-split] #'+eshell/split - [remap evil-window-vsplit] #'+eshell/vsplit)) + [remap split-window-below] #'+eshell/split-below + [remap split-window-right] #'+eshell/split-right + [remap evil-window-split] #'+eshell/split-below + [remap evil-window-vsplit] #'+eshell/split-right)) (add-hook 'eshell-first-time-mode-hook #'+eshell|init-keymap))