diff options
Diffstat (limited to 'Emacs/.config/emacs/elisp/eshell-prompt.el')
-rw-r--r-- | Emacs/.config/emacs/elisp/eshell-prompt.el | 240 |
1 files changed, 171 insertions, 69 deletions
diff --git a/Emacs/.config/emacs/elisp/eshell-prompt.el b/Emacs/.config/emacs/elisp/eshell-prompt.el index d0a4e91..9267469 100644 --- a/Emacs/.config/emacs/elisp/eshell-prompt.el +++ b/Emacs/.config/emacs/elisp/eshell-prompt.el @@ -19,25 +19,86 @@ ;;; Commentary: -;; We provide a function +eshell-prompt which generates a prompt on +;; We provide a function ep which generates a prompt on ;; demand. ;;; Code: -(defvar +eshell-prompt/user-prompt "𝜆> " +(defvar ep/user-prompt " λ " "Prompt for user to input.") -(defvar +eshell-prompt/dir-colour "deepskyblue") -(defvar +eshell-prompt/success-colour "forestgreen") -(defvar +eshell-prompt/failure-colour "red") - -(defun +eshell-prompt/--colour-on-last-command () +(defvar ep/pwd-max-len 30) +(defvar ep/git-branch-max-len 5) + +(defvar ep/dir-colour "deepskyblue") +(defvar ep/success-colour "green2") +(defvar ep/failure-colour "red") +(defvar ep/branch-name-colour "LightSalmon") +(defvar ep/pipe-colour "green4") +(defvar ep/ahead-colour "dodger blue") +(defvar ep/remote-colour "DarkGoldenrod") + +(defun ep/make-prompt () + (let ((git (ep/--git-status))) + (thread-last + `(("┌──" :foreground ,ep/pipe-colour) + "[" + (,(ep/--user-and-remote) :foreground ,ep/remote-colour) + (,(ep/--pwd) + :foreground ,ep/dir-colour) + ,(if (string= git "") + "" + (concat "]─[" git)) + "]" + "\n" + ("└─>" :foreground ,ep/pipe-colour) + (,ep/user-prompt :foreground ,(ep/--colour-on-last-command))) + (mapconcat + #'(lambda (item) + (thread-last + (propertize (car item) + 'font-lock-face (cdr item) + 'front-sticky '(font-lock-face read-only) + 'rear-nonsticky '(font-lock-face read-only)) + (if (not (listp item)) + item))))))) + +(defun ep/--colour-on-last-command () "Returns an Emacs colour based on ESHELL-LAST-COMMAND-STATUS." (if (zerop eshell-last-command-status) - +eshell-prompt/success-colour - +eshell-prompt/failure-colour)) + ep/success-colour + ep/failure-colour)) + +(defun ep/--with-fg-colour (s colour) + "Helper which propertises a string `s' with foreground colour `colour'" + (propertize s 'font-lock-face `(:foreground ,colour))) + +(defun ep/--user-and-remote () + "If in a remote directory, return a string representing that host, +otherwise empty string." + (if (file-remote-p default-directory) + (let ((user (file-remote-p default-directory 'user)) + (host (file-remote-p default-directory 'host))) + (thread-first + (if user + (format "%s@%s" user host) + host) + (concat ":"))) + "")) + +(defun ep/--git-status () + "Returns a completely formatted string of form +BRANCH-NAME(REMOTE-STATUS)(CHANGES)." + (let ((git-branch (ep/--git-branch-name))) + (if (null git-branch) + "" + (format + "%s(%s)(%s)" + (ep/--with-fg-colour git-branch ep/branch-name-colour) + (ep/--git-remote-status) + (ep/--git-change-status))))) -(defun +eshell-prompt/--git-remote-status () +(defun ep/--git-remote-status () "Returns a propertized string for the status of a repository in comparison to its remote. 3 differing strings are returned dependent on: @@ -53,67 +114,108 @@ behind or ahead the local repository is." (status (nth 3 branch-status)) (diff (cl-position "by" branch-status :test #'string=))) (if (null diff) - (propertize "=" 'font-lock-face `(:foreground ,+eshell-prompt/success-colour)) - (let ((n (nth (+ 1 diff) branch-status))) - (concat - (cond - ((string= status "ahead") - (propertize "→" 'font-lock-face '(:foreground "dodger blue"))) - ((string= status "behind") - (propertize "←" 'font-lock-face '(:foreground "red")))) - n))))) - -(defun +eshell-prompt/--git-change-status () + (ep/--with-fg-colour "=" ep/success-colour) + (concat + (cond + ((string= status "ahead") + (ep/--with-fg-colour "→" ep/ahead-colour)) + ((string= status "behind") + (ep/--with-fg-colour "←" ep/failure-colour))) + (nth (1+ diff) branch-status))))) + +(defun ep/--git-change-status () "Returns a propertized string for the condition of the worktree in -a repository. If there are no changes i.e. the worktree is clean -then a green tick is returned, but if there are changes then the -number of files affected are returned in red." - (let* ((git-cmd "git status -s") - (command-output (split-string (shell-command-to-string git-cmd) "\n")) - (changed-files (- (length command-output) 1))) - (if (= changed-files 0) - (propertize "✓" - 'font-lock-face - `(:foreground ,+eshell-prompt/success-colour)) - (propertize (number-to-string changed-files) - 'font-lock-face - `(:foreground ,+eshell-prompt/failure-colour))))) - -(defun +eshell-prompt/--git-status () - "Returns a completely formatted string of -form (BRANCH-NAME<CHANGES>[REMOTE-STATUS])." - (let ((git-branch (thread-last - (split-string (shell-command-to-string "git branch") "\n") - (cl-remove-if (lambda (s) (= (length s) 0))) - (cl-find-if (lambda (s) (string= "*" (substring s 0 1))))))) - (if (null git-branch) - "" - (format - "(%s<%s>[%s])" - (nth 2 (split-string git-branch "\n\\|\\*\\| ")) - (+eshell-prompt/--git-change-status) - (+eshell-prompt/--git-remote-status))))) - -(defun +eshell-prompt/make-prompt () - (let ((git (+eshell-prompt/--git-status))) - (mapconcat - (lambda (item) - (if (listp item) - (propertize (car item) - 'font-lock-face (cdr item) - 'front-sticky '(font-lock-face read-only) - 'rear-nonsticky '(font-lock-face read-only)) - item)) - (list - "[" - `(,(abbreviate-file-name (eshell/pwd)) :foreground ,+eshell-prompt/dir-colour) - "]" - (if (string= git "") - "" - (concat " " git)) - "\n" - (list "𝜆> " ':foreground (+eshell-prompt/--colour-on-last-command)))))) +a repository. +If there are no changes i.e. the worktree is clean then a green tick is +returned. + +If there are changes then we characterise it by the following parameters: +- staged changes in green +- unstaged but tracked changes in blue +- untracked files in red +" + (let* ((git-cmd "git status -s") + (command-output (thread-first + (shell-command-to-string git-cmd) + (split-string "\n") + butlast)) + (status-codes (mapcar #'(lambda (s) (cons (substring s 0 1) (substring s 1 2))) + command-output)) + (count-f (lambda (coll) (thread-first + (lambda (x) (not (or (string= x "?") (string= x " ")))) + (cl-count-if + coll)))) + (total (length status-codes)) + (staged (funcall count-f (mapcar #'car status-codes))) + (modified (funcall count-f (mapcar #'cdr status-codes))) + (not-tracked (cl-count-if (lambda (x) (string= (cdr x) "?")) status-codes))) + (if (= total 0) + (ep/--with-fg-colour "✓" ep/success-colour) + (thread-last + (list + (ep/--with-fg-colour (number-to-string staged) ep/success-colour) + (ep/--with-fg-colour (number-to-string modified) ep/ahead-colour) + (ep/--with-fg-colour (number-to-string not-tracked) ep/failure-colour)) + (cl-remove-if #'(lambda (s) (string= s "0"))) + (mapconcat #'(lambda (s) (concat s "/"))))))) + +(defun ep/--git-branch-name () + "Get the branch name of the current working directory. + +If a deteached head, return the SHA." + (let* ((branch-name (thread-last + (split-string (shell-command-to-string "git branch") "\n") + (cl-remove-if #'(lambda (s) (thread-last (length s) (= 0)))) + (cl-find-if #'(lambda (s) (thread-last (substring s 0 1) (string= "*")))))) + (branch-name (thread-last + (substring branch-name 2) + (if (null branch-name) nil)))) + (if branch-name + (ep/--abbreviate-str + (cond + ((string= "(" (substring branch-name 0 1)) + (replace-regexp-in-string + "\n$" "" + (shell-command-to-string "git rev-parse --short HEAD"))) + (t branch-name)) + "-" + ep/git-branch-max-len)))) + +(defun ep/--pwd () + (let ((pwd (thread-last (eshell/pwd) + tramp-file-local-name + abbreviate-file-name))) + (ep/--abbreviate-str pwd "/" ep/pwd-max-len))) + +(defun ep/--abbreviate-str (to-abbrev sep max-len) + (if (<= (length to-abbrev) max-len) + to-abbrev + (let ((str "") + (len (length to-abbrev)) + (components (split-string to-abbrev sep))) + (while (and (> len max-len) + (cdr components)) + (let* ((comp (car components)) + (ab-comp (cond + ((= 0 (length comp)) "") + ((= 1 (length comp)) comp) + ((char-equal (elt comp 0) ?.) + (substring comp 0 3)) + (t + (substring comp 0 2))))) + (setq str (concat str ab-comp sep) + len (- len (length ab-comp)) + components (cdr components)))) + (thread-last + components + (cl-reduce #'(lambda (a b) (concat a sep b))) + (if (null components) "") + (concat str))))) (provide 'eshell-prompt) ;;; eshell-prompt.el ends here + +;; Local Variables: +;; read-symbol-shorthands: (("ep" . "eshell-prompt")) +;; End: |