;;; syd-eshell.el -*- lexical-binding: t; -*- (require 'ring) (require 'cl-lib) (defvar eshell-buffer-name "*eshell*") (defvar syd-eshell-buffers (make-ring 25) "List of open eshell buffers.") (defun syd-eshell-buffers () "TODO" (ring-elements syd-eshell-buffers)) ;;;###autoload (defun syd-eshell-run-command (command &optional buffer) "TODO" (let ((buffer (or buffer (if (eq major-mode 'eshell-mode) (current-buffer) (cl-find-if #'buffer-live-p (syd-eshell-buffers)))))) (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)))) ;;;###autoload (defun syd-eshell/toggle (arg &optional command) "Toggle eshell popup window." (interactive "P") (let ((eshell-buffer (get-buffer-create (format "*eshell-popup:%s*" (if (bound-and-true-p persp-mode) (safe-persp-name (get-current-persp)) "main")))) confirm-kill-processes current-prefix-arg) (when arg (when-let* ((win (get-buffer-window eshell-buffer))) (delete-window win)) (when (buffer-live-p eshell-buffer) (with-current-buffer eshell-buffer (fundamental-mode) (erase-buffer)))) (if-let* ((win (get-buffer-window eshell-buffer))) (let (confirm-kill-processes) (delete-window win) (ignore-errors (kill-buffer eshell-buffer))) (with-current-buffer eshell-buffer (syd-mark-buffer-as-real) (if (eq major-mode 'eshell-mode) (run-hooks 'eshell-mode-hook) (eshell-mode)) (when command (syd-eshell-run-command command eshell-buffer))) (pop-to-buffer eshell-buffer) (when (bound-and-true-p evil-mode) (call-interactively #'evil-append-line))))) (defun syd-eshell-adapt-bash-aliases () "Very sloppily convert aliases defined in Bash to an Eshell alias file." (interactive) (save-window-excursion (let ((err-buf (generate-new-buffer "*adapt-bash-aliases-err*")) (result-buf (generate-new-buffer "*adapted-bash-aliases*"))) (with-current-buffer result-buf (insert "# Automatically generated by syd-eshell-adapt-bash-aliases\n")) (with-temp-buffer ;; Aliases are only loaded when bash is in interactive mode. (shell-command "bash -ic alias" (current-buffer) err-buf) (goto-char (point-min)) (while (re-search-forward (rx bol "alias " (group (+ alphanumeric)) "='" (group (* (not ?\'))) "'") nil t) (let ((eshell-alias (format "alias %s %s $*\n" (match-string 1) (match-string 2)))) (with-current-buffer result-buf (insert eshell-alias))))) (unless (= 0 (buffer-size err-buf)) (message "Some errors occured whilst fetching Bash's aliases:") (message (with-current-buffer err-buf (buffer-string)))) (kill-buffer err-buf) result-buf))) (defun syd-eshell-C-d () "Imitate the typical 'C-d' behaviour in other shells. Quits Eshell when the input is empty." (interactive) (when (and (eolp) (looking-back eshell-prompt-regexp nil)) (eshell-life-is-too-much))) (defun syd-eshell--init-ui-hacks () (defun syd-eshell-remove-fringes-h () (set-window-fringes nil 0 0) (set-window-margins nil 1 nil)) (defun syd-eshell-enable-text-wrapping-h () (visual-line-mode +1) (set-display-table-slot standard-display-table 0 ?\ )) (add-hook 'eshell-mode-hook #'syd-eshell-remove-fringes-h) (add-hook 'eshell-mode-hook #'syd-eshell-enable-text-wrapping-h) (with-eval-after-load 'hide-mode-line (add-hook 'eshell-mode-hook #'hide-mode-line-mode)) ;; Remove hscroll-margin in shells, otherwise you get jumpiness when the ;; cursor comes close to the left/right edges of the window. (defun syd-eshell-disable-hscroll-margin () (setq hscroll-margin 0)) (add-hook 'eshell-mode-hook #'syd-eshell-disable-hscroll-margin)) (use-package shrink-path :defer t) (defface syd-eshell-local-name '((t (:inherit font-lock-constant-face))) "Face used by the Eshell prompt for the CWD's non-TRAMP part. See `syd-eshell--prompt-fn'" :group 'eshell) (defface syd-eshell-tramp-prefix '((t (:inherit font-lock-comment-face))) "Face used by the Eshell prompt for the CWD's TRAMP prefix. See `syd-eshell--prompt-fn'." :group 'eshell) (defun syd-eshell--prompt-fn () "See `eshell-prompt-function'." (require 'shrink-path) (require 'syd-file) (-let (((tramp-prefix . local-name) (syd-split-tramp-file-name (eshell/pwd)))) (concat (unless (bobp) "\n") (when tramp-prefix (propertize tramp-prefix 'face 'syd-eshell-tramp-prefix)) (propertize (if (equal local-name "~") local-name (abbreviate-file-name (shrink-path-file local-name))) 'face 'syd-eshell-local-name) (propertize " $" 'face (if (zerop eshell-last-command-status) 'success 'error)) " "))) (defvar syd-eshell--prompt-regexp (rx bol (* (not (any "\n$"))) " $ ")) (set-popup-rule! "^\\*eshell-popup" :vslot -5 :size 13 :select t :modeline nil :quit nil :ttl nil) (use-package eshell :init (defvar syd-eshell-data-dir (file-name-concat syd-data-dir "eshell")) (make-directory syd-eshell-data-dir t) :custom ((eshell-banner-message '(format "🦌 %s %s }:3\n" (propertize (format " %s " (string-trim (buffer-name))) 'face 'mode-line-highlight) (propertize (current-time-string) 'face 'font-lock-keyword-face))) (eshell-scroll-to-bottom-on-input 'all) (eshell-scroll-to-bottom-on-output 'all) (eshell-kill-processes-on-exit t) (eshell-hist-ignoredups t) (eshell-glob-case-insensitive t) (eshell-error-if-no-glob t) (eshell-history-file-name (file-name-concat syd-eshell-data-dir "history")) (eshell-last-dir-ring-file-name (file-name-concat syd-eshell-data-dir "lastdir")) (eshell-prompt-function #'syd-eshell--prompt-fn) (eshell-prompt-regexp syd-eshell--prompt-regexp)) :general (:keymaps 'syd-leader-open-map "e" #'syd-eshell/toggle) (:keymaps 'eshell-mode-map :states 'insert "C-d" #'syd-eshell-C-d) (:keymaps 'eshell-mode-map :states '(normal insert) "[ [" #'eshell-previous-prompt "] ]" #'eshell-next-prompt "C-j" #'eshell-next-matching-input-from-input "C-k" #'eshell-previous-matching-input-from-input) :config ;; When cd'd into a TRAMP remote, automatically expand '/' to the TRAMP ;; notation referring to the remote's root. (add-to-list 'eshell-modules-list 'eshell-elecslash) (require 'syd-buffers) (add-hook 'eshell-mode-hook #'syd-mark-buffer-as-real) ;; UI enhancements. (syd-eshell--init-ui-hacks)) (provide 'syd-eshell)