diff --git a/elisp/eshell-starship.el b/elisp/eshell-starship.el index 7443f32..be5ac16 100644 --- a/elisp/eshell-starship.el +++ b/elisp/eshell-starship.el @@ -5,6 +5,7 @@ (require 'vc) (require 'vc-git) (require 'eshell) +(require 'cl-lib) (defun eshell-starship--git-process-lines (&rest flags) "Run `vc-git-program' and return an array of its output lines. @@ -43,68 +44,56 @@ Example: (if-let ((worktree (vc-root-dir)) (parent (file-name-parent-directory worktree))) (file-relative-name default-directory parent) - (eshell-starship--replace-home-with-tilda default-directory)))) + (eshell-starship--replace-home-with-tilda + default-directory)))) -(defun eshell-starship--prompt-status-char-for-branch (branch remote) - "Get the status char representing the relation between BRANCH and REMOTE." - (let ((lines (eshell-starship--git-process-lines - "rev-list" - "--left-right" - (concat branch "..." remote))) - (to-remote nil) - (to-local nil)) - (dolist (line lines) - (if-let (((not (string-empty-p line))) - (dir-char (aref line 0))) - (if (= dir-char ?<) - (setq to-remote t) - (setq to-local t)))) - (cond - ((and to-remote to-local) ?󰹺) - (to-remote ?󰜷) - (to-local ?󰜮)))) +(defun eshell-starship--prompt-current-branch-status (status-line) + "Get the status char for the current branch and its remote. +STATUS-LINE is the first line of output from \"git status --porcelain=v1 -b\"." + (when (string-match + "\\[\\(?:ahead \\([0-9]+\\)\\)?,? ?\\(?:behind \\([0-9]+\\)\\)?\\]$" + status-line) + (let ((ahead (match-string 1 status-line)) + (behind (match-string 2 status-line))) + (cond + ((and ahead behind) ?󰹺) + (ahead ?󰜷) + (behind ?󰜮))))) - -(defun eshell-starship--prompt-current-branch-status () - "Get the status char for the current branch and its remote." - (let ((refs (eshell-starship--git-process-lines - "for-each-ref" - "--format=%(HEAD)%00%(refname:short)%00%(upstream:short)" - "refs/heads"))) - (catch 'break - (dolist (ref refs) - (if-let ((split-ref (split-string ref "\0" nil nil)) - ((equal (car split-ref) "*"))) - (throw 'break (eshell-starship--prompt-status-char-for-branch - (cadr split-ref) - (caddr split-ref)))))))) +(defun eshell-starship--prompt-git-has-stash () + "Return t if the current git directory has a stash, nil otherwise." + (= (process-file vc-git-program nil nil nil + "rev-parse" "--verify" "refs/stash") 0)) (defun eshell-starship--prompt-git-state-chars () "Get chars, like + and ✘ for `eshell-starship--prompt-function'." - (let ((lines (eshell-starship--git-process-lines "status" "--porcelain=v1")) - (branch-status (eshell-starship--prompt-current-branch-status)) - (status-arr)) - (dolist (line lines) + (let* ((lines (eshell-starship--git-process-lines "status" "--porcelain=v1" "-b")) + (branch-status (eshell-starship--prompt-current-branch-status + (car lines))) + (status-arr)) + (dolist (line (cdr lines)) (cl-loop with fields = (string-split line " " t " *") with status-str = (car-safe fields) for status-char across status-str do (cond ((or (= status-char ?M) (= status-char ?T)) - (add-to-list 'status-arr ?!)) + (push ?! status-arr)) ((= status-char ??) - (add-to-list 'status-arr ??)) + (push ?? status-arr)) ((or (= status-char ?A) (= status-char ?C)) - (add-to-list 'status-arr ?+)) + (push ?+ status-arr)) ((= status-char ?D) - (add-to-list 'status-arr ?)) + (push ? status-arr)) ((= status-char ?R) - (add-to-list 'status-arr ?»)) + (push ?» status-arr)) ((= status-char ?U) - (add-to-list 'status-arr ?=))))) + (push ?= status-arr))))) + (when (eshell-starship--prompt-git-has-stash) + (push ?$ status-arr)) (sort status-arr #'<) (when branch-status (push branch-status status-arr)) - (apply 'string status-arr))) + (apply 'string (seq-uniq status-arr)))) (defun eshell-starship--prompt-git-get-operation () "Return the current git operation. For example, a revert." @@ -198,7 +187,7 @@ END-TIME is the time when the command finished executing." ;;;###autoload (define-minor-mode eshell-starship-prompt-mode - "Minor mode to make eshell prompts look like starship (https://starship.rs)" + "Minor mode to make eshell prompts look like starship (https://starship.rs)." :global nil :init-value nil :interactive (eshell-mode)