feat(emacs): Eshell stuff

- Fancify prompt
  - Shows success of last command.
  - TRAMP prefix is made distinct.
  - CWD is abbreviated.
- cd to project root (`cdp`).
- Imitate using `C-d` / send-EOF to exit.
- Opening popup shell puts user in insert state.
- Fixes TRAMP customisation.

Nicely done! }:3
This commit is contained in:
Madeleine Sydney
2025-02-16 18:50:00 -07:00
parent a0495c6df3
commit 6c9213d532
8 changed files with 182 additions and 44 deletions

View File

@@ -0,0 +1,33 @@
alias ga git add $*
alias gb git branch $*
alias gc git commit $*
alias gcl git clone $*
alias gco git checkout $*
alias gd git diff $*
alias gl git log $*
alias glo git log --pretty=oneline $*
alias glol git log --graph --oneline --decorate $*
alias gp git push $*
alias gr git remote $*
alias grs git remote show $*
alias gs git status $*
alias gtd git tag --delete $*
alias jb jj bookmark $*
alias jd jj describe $*
alias jdi jj diff $*
alias je jj edit $*
alias jgcl jj git clone $*
alias jgp jj git push $*
alias jgr jj git remote $*
alias jl jj log $*
alias jn jj new $*
alias js jj status --no-pager $*
alias jsp jj split $*
alias l ls -alh $*
alias ll ls -l $*
alias ls ls --color=tty $*
alias pass /nix/store/hqhi6dgl7p16v49ymg2hwkgl844092fb-passage-1.7.4a2/bin/passage $*
alias cdp syd-cd-project

View File

@@ -1,3 +0,0 @@
doge
soge
grep -r xref

View File

@@ -135,4 +135,15 @@ If FORCE-P, delete without confirmation."
base-file base-file
conflict-file))) conflict-file)))
(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)))
(provide 'syd-file) (provide 'syd-file)

View File

@@ -8,5 +8,9 @@
(when-let* ((project (project-current nil dir))) (when-let* ((project (project-current nil dir)))
(project-root project))) (project-root project)))
(defun syd-cd-project ()
"Change the working directory to the root of the current project."
(cd (syd-project-root)))
(provide 'syd-project) (provide 'syd-project)
;;; syd-project.el ends here ;;; syd-project.el ends here

View File

@@ -60,40 +60,44 @@
(eshell-mode)) (eshell-mode))
(when command (when command
(syd-eshell-run-command command eshell-buffer))) (syd-eshell-run-command command eshell-buffer)))
(pop-to-buffer eshell-buffer)))) (pop-to-buffer eshell-buffer)
(when (bound-and-true-p evil-mode)
(call-interactively #'evil-append-line)))))
(use-package eshell (defun syd-eshell-adapt-bash-aliases ()
:custom "Very sloppily convert aliases defined in Bash to an Eshell alias file."
((eshell-banner-message (interactive)
'(format "🦌 %s %s }:3\n" (save-window-excursion
(propertize (format " %s " (string-trim (buffer-name))) (let ((err-buf (generate-new-buffer "*adapt-bash-aliases-err*"))
'face 'mode-line-highlight) (result-buf (generate-new-buffer "*adapted-bash-aliases*")))
(propertize (current-time-string) (with-current-buffer result-buf
'face 'font-lock-keyword-face))) (insert "# Automatically generated by syd-eshell-adapt-bash-aliases\n"))
(eshell-scroll-to-bottom-on-input 'all) (with-temp-buffer
(eshell-scroll-to-bottom-on-output 'all) ;; Aliases are only loaded when bash is in interactive mode.
(eshell-kill-processes-on-exit t) (shell-command "bash -ic alias" (current-buffer) err-buf)
(eshell-hist-ignoredups t) (goto-char (point-min))
(eshell-glob-case-insensitive t) (while (re-search-forward
(eshell-error-if-no-glob t) (rx bol "alias " (group (+ alphanumeric)) "='"
(eshell-history-file-name (file-name-concat (group (* (not ?\'))) "'")
syd-data-dir "eshell" "history")) nil t)
(eshell-last-dir-ring-file-name (file-name-concat (let ((eshell-alias (format "alias %s %s $*\n"
syd-data-dir "eshell" "lastdir"))) (match-string 1)
:general (match-string 2))))
(:keymaps 'syd-leader-open-map (with-current-buffer result-buf
"e" #'syd-eshell/toggle) (insert eshell-alias)))))
(:keymaps 'eshell-mode-map (unless (= 0 (buffer-size err-buf))
:states '(normal insert) (message "Some errors occured whilst fetching Bash's aliases:")
"C-j" #'eshell-next-matching-input-from-input (message (with-current-buffer err-buf (buffer-string))))
"C-k" #'eshell-previous-matching-input-from-input) (kill-buffer err-buf)
result-buf)))
:config (defun syd-eshell-C-d ()
(require 'syd-buffers) "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)))
(add-hook 'eshell-mode-hook #'syd-mark-buffer-as-real) (defun syd-eshell--init-ui-hacks ()
;; UI enhancements.
(defun syd-eshell-remove-fringes-h () (defun syd-eshell-remove-fringes-h ()
(set-window-fringes nil 0 0) (set-window-fringes nil 0 0)
(set-window-margins nil 1 nil)) (set-window-margins nil 1 nil))
@@ -112,7 +116,87 @@
(setq hscroll-margin 0)) (setq hscroll-margin 0))
(add-hook 'eshell-mode-hook #'syd-eshell-disable-hscroll-margin)) (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" (set-popup-rule! "^\\*eshell-popup"
:vslot -5 :size 0.35 :select t :modeline nil :quit nil :ttl nil) :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)
"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) (provide 'syd-eshell)

View File

@@ -52,7 +52,12 @@
;; In imitation of Vim's :mes[sages] command, define an Evil analogue to show ;; In imitation of Vim's :mes[sages] command, define an Evil analogue to show
;; the echo area. ;; the echo area.
(evil-ex-define-cmd "mes[sages]" #'view-echo-area-messages) (defun syd-evil-messages ()
(interactive)
(view-echo-area-messages)
(with-current-buffer messages-buffer-name
(evil-motion-state 1)))
(evil-ex-define-cmd "mes[sages]" #'syd-evil-messages)
;; On ESC, remove highlighted search results. ;; On ESC, remove highlighted search results.
(defun syd-evil-nohl-h () (defun syd-evil-nohl-h ()

View File

@@ -1,10 +1,9 @@
;;; syd-tramp.el -*- lexical-binding: t; -*- ;;; syd-tramp.el -*- lexical-binding: t; -*-
(use-package tramp (with-eval-after-load 'tramp
:defer t (setq tramp-persistency-file-name
:custom ((tramp-persistency-file-name (file-name-concat syd-cache-dir (file-name-concat syd-cache-dir "tramp"))
"tramp")) (setq tramp-auto-save-directory
(tramp-auto-save-directory (file-name-concat syd-cache-dir "tramp-autosave/")))
(file-name-concat syd-cache-dir "tramp-autosave/"))))
(provide 'syd-tramp) (provide 'syd-tramp)

View File

@@ -64,7 +64,11 @@
(native-comp-async-report-warnings-errors 'silent) (native-comp-async-report-warnings-errors 'silent)
;; Don't recenter the view unless >10 lines are scrolled off-screen ;; Don't recenter the view unless >10 lines are scrolled off-screen
;; in a single movement. ;; in a single movement.
(scroll-conservatively 10)) (scroll-conservatively 10)
;; In modes making use of `recenter-top-bottom' (e.g. Comint,
;; Eshell), this will make the command behave more like a plain old
;; `clear` invocation.
(recenter-positions '(top)))
:config :config
;; Disable the menu bar, scroll bar, and tool bar. ;; Disable the menu bar, scroll bar, and tool bar.
(menu-bar-mode -1) (menu-bar-mode -1)
@@ -75,7 +79,7 @@
:disabled :disabled
:unless noninteractive :unless noninteractive
:commands persp-switch-to-buffer :commands persp-switch-to-buffer
:hook (on-init-ui . persp-mode) :hook (on-init-ui-hook . persp-mode)
:config :config
(setq persp-autokill-buffer-on-remove 'kill-weak (setq persp-autokill-buffer-on-remove 'kill-weak
persp-reset-windows-on-nil-window-conf nil persp-reset-windows-on-nil-window-conf nil
@@ -89,14 +93,15 @@
persp-auto-resume-time -1 ; Don't auto-load on startup persp-auto-resume-time -1 ; Don't auto-load on startup
persp-auto-save-opt (if noninteractive 0 1))) persp-auto-save-opt (if noninteractive 0 1)))
(defun syd-init-ui-hacks () (syd-add-hook 'on-init-ui-hook
(defun syd-init-ui-hacks ()
(set-popup-rule! "*Backtrace*" (set-popup-rule! "*Backtrace*"
:size #'doom-popup-shrink-to-fit) :size #'doom-popup-shrink-to-fit)
(set-popup-rule! "*Messages*" (set-popup-rule! "*Messages*"
:ttl nil :ttl nil
:quit t) :quit t)
(evil-set-initial-state 'debugger-mode 'normal)) ;; Required for :quit t to do anything.
(evil-set-initial-state 'messages-buffer-mode 'motion)
(add-hook 'on-init-ui-hook #'syd-init-ui-hacks) (evil-set-initial-state 'debugger-mode 'normal)))
(provide 'syd-ui) (provide 'syd-ui)