diff --git a/modules/home/users/msyds/emacs/init.el b/modules/home/users/msyds/emacs/init.el index 728705a..9896df7 100755 --- a/modules/home/users/msyds/emacs/init.el +++ b/modules/home/users/msyds/emacs/init.el @@ -60,4 +60,5 @@ syd/wgrep syd/nix syd/backup - syd/agda)) + syd/agda + syd/eshell)) diff --git a/modules/home/users/msyds/emacs/lisp/syd/eshell.el b/modules/home/users/msyds/emacs/lisp/syd/eshell.el new file mode 100644 index 0000000..ef1efec --- /dev/null +++ b/modules/home/users/msyds/emacs/lisp/syd/eshell.el @@ -0,0 +1,182 @@ +;;; syd-eshell.el -*- lexical-binding: t; -*- + +(require 'syd/base) +(require 'syd/popups) +(require 'ring) +(require 'cl-lib) + +(defun syd-toggle-eshell (arg &optional command) + "Toggle eshell popup window." + (interactive "P") + (let ((eshell-buffer (get-buffer-create "*eshell-popup*")) + 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 + (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 () + (syd-add-hook 'eshell-mode-hook + (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 ?\ )) + ;; 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))) + (with-eval-after-load 'hide-mode-line + (add-hook 'eshell-mode-hook #'hide-mode-line-mode))) + +(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) + (-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$"))) " $ ")) + +(defun syd-split-tramp-file-name (file-name) + "Split FILE-NAME into (TRAMP-PREFIX . LOCAL-NAME). Returns (nil . FILE-NAME) +if FILE-NAME has no TRAMP prefix." + (if (tramp-tramp-file-p file-name) + (let* ((dissected (tramp-dissect-file-name file-name t)) + (localname (tramp-file-name-localname dissected))) + (setf (tramp-file-name-localname dissected) nil) + (cons (tramp-make-tramp-file-name dissected) + localname)) + (cons nil file-name))) + +(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 nil) + (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-toggle-eshell) + (:keymaps 'eshell-mode-map + :states 'insert + "C-d" #'syd-eshell-C-d) + (:keymaps 'eshell-mode-map + :states 'motion + "[ [" #'eshell-previous-prompt + "] ]" #'eshell-next-prompt) + (:keymaps 'eshell-mode-map + :states '(normal insert) + "C-j" #'eshell-next-matching-input-from-input + "C-k" #'eshell-previous-matching-input-from-input + "C-s" #'consult-history) + :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) + + ;; UI enhancements. + (syd-eshell--init-ui-hacks) + (syd-push shackle-rules + '("*eshell-popup*" + :align below + :size 13 + :select t + :inhibit-window-quit t))) + +(provide 'syd/eshell)