Finish eshell-starship.el refactor

This commit is contained in:
Alexander Rosenberg 2024-01-18 16:11:13 -08:00
parent 7310a981e7
commit 5c74237b4f
Signed by: Zander671
GPG Key ID: 5FD0394ADBD72730

View File

@ -7,15 +7,6 @@
(require 'eshell) (require 'eshell)
(require 'cl-lib) (require 'cl-lib)
(defun eshell-starship--git-process-lines (&rest flags)
"Run `vc-git-program' and return an array of its output lines.
FLAGS are passed to `vc-git-program' as its arguments."
(with-temp-buffer
(apply 'vc-git-command t 0 nil flags)
(if (zerop (buffer-size))
'()
(string-lines (buffer-substring-no-properties 1 (buffer-size))))))
(defun eshell-starship--replace-home-with-tilda (path) (defun eshell-starship--replace-home-with-tilda (path)
"If PATH beings with $HOME (the environment variable), replace it with ~." "If PATH beings with $HOME (the environment variable), replace it with ~."
(let ((home (getenv "HOME"))) (let ((home (getenv "HOME")))
@ -26,7 +17,7 @@ FLAGS are passed to `vc-git-program' as its arguments."
(concat "~/" (seq-subseq path (length home))) (concat "~/" (seq-subseq path (length home)))
path)))) path))))
(defun eshell-starship--prompt-cut-path (num path) (defun eshell-starship--limit-path-parts (num path)
"Cut PATH down to NUM components. "Cut PATH down to NUM components.
Example: Example:
/this/is/a/path 3-> is/a/path" /this/is/a/path 3-> is/a/path"
@ -38,89 +29,107 @@ Example:
"/") "/")
(string-join (last parts num) "/")))) (string-join (last parts num) "/"))))
(defun eshell-starship--prompt-get-dir () (defun eshell-starship--get-current-dir ()
"Get dir for `eshell-starship--prompt-function'." "Get dir for `eshell-starship--prompt-function'."
(eshell-starship--prompt-cut-path 3 (eshell-starship--limit-path-parts
(if-let ((worktree (vc-root-dir)) 3 (if-let ((worktree (vc-root-dir))
(parent (file-name-parent-directory worktree))) (parent (file-name-parent-directory worktree)))
(file-relative-name default-directory parent) (file-relative-name default-directory parent)
(eshell-starship--replace-home-with-tilda (eshell-starship--replace-home-with-tilda
default-directory)))) default-directory))))
(defun eshell-starship--prompt-current-branch-status (status-line) (defun eshell-starship--git-parse-status-headers ()
"Parse the status headers (read from the current buffer).
The headers are as described in the porcelain v2 section of the git-status(3)
man page.
The return value is a list of the form (oid head upstream ahead behind stash)"
(let ((oid nil)
(head nil)
(upstream nil)
(ahead nil)
(behind nil)
(stash nil))
(while (and (char-after) (= (char-after) ?#))
(forward-char 2)
(cond
((looking-at "branch\\.oid ")
(setq oid (buffer-substring-no-properties
(match-end 0)
(pos-eol))))
((looking-at "branch\\.head ")
(setq head (buffer-substring-no-properties
(match-end 0)
(pos-eol))))
((looking-at "branch\\.upstream ")
(setq upstream (buffer-substring-no-properties
(match-end 0)
(pos-eol))))
((looking-at "branch\\.ab ")
(let ((ab-str (buffer-substring-no-properties
(match-end 0)
(pos-eol))))
(when (string-match "\\(+[0-9]+\\) \\(-[0-9]+\\)$"
ab-str)
(setq ahead (string-to-number (match-string 1 ab-str))
behind (string-to-number (match-string 2 ab-str))))))
((looking-at "stash ")
(setq stash (string-to-number (buffer-substring-no-properties
(match-end 0)
(pos-eol))))))
(forward-line))
(list oid head upstream (or ahead 0) (or behind 0) stash)))
(defun eshell-starship--git-interpret-file-status (x y)
"Return the prompt character for the status X and Y.
A description of X and Y can be found in the git-status(3) man page."
(cond
((or (= x ?D) (= y ?D))
?)
((or (= x ?R) (= y ?R))
)
((= y ?M)
?!)
((or (= x ?A) (= x ?M))
?+)))
(defun eshell-starship--git-interpret-branch-status (ahead behind)
"Get the status char for the current branch and its remote. "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\"." AHEAD should evaluate to t if the current branch is ahead of its remote, and
(when (string-match BEHIND should evaluate to t if the current branch is behind its remote."
"\\[\\(?:ahead \\([0-9]+\\)\\)?,? ?\\(?:behind \\([0-9]+\\)\\)?\\]$"
status-line)
(let ((ahead (match-string 1 status-line))
(behind (match-string 2 status-line)))
(cond (cond
((and ahead behind) ?󰹺) ((and ahead behind) "󰹺")
(ahead ?󰜷) (ahead "󰜷")
(behind ?󰜮))))) (behind "󰜮")))
(defun eshell-starship--prompt-git-has-stash () (defun eshell-starship--git-file-status (stash ahead behind)
"Return t if the current git directory has a stash, nil otherwise." "Get the file status string for the git prompt module.
(zerop (process-file vc-git-program nil nil nil STASH should be t if there is current stashed data stash. AHEAD and BEHIND
"rev-parse" "--verify" "refs/stash"))) should be as for `eshell-starship--git-interpret-branch-status'."
(let ((merge-conflicts nil)
(defun eshell-starship--prompt-git-state-chars () (status-chars nil))
"Get chars, like + and ✘ for `eshell-starship--prompt-function'." (while (not (= (pos-bol) (pos-eol)))
(with-temp-buffer
(when (zerop (vc-git-command t nil nil "status" "--porcelain=v1" "-b"))
(goto-char (point-min))
(cl-loop with command-error-function = nil
with status-arr = nil
with first-line = (buffer-substring-no-properties
(point) (pos-eol))
;; account for newline at end
with line-count = (car (buffer-line-statistics))
with cur-buf = (current-buffer)
do (forward-line)
for x_status = (char-after)
for y_status = (char-after (1+ (point)))
until (> (line-number-at-pos) line-count)
do
(cond (cond
((or (= ?D x_status y_status) ((= (char-after) ??)
(= ?A x_status y_status) (push ?? status-chars))
(= ?U x_status y_status) ((= (char-after) ?u)
(and (= ?A x_status) (= ?U y_status)) (setq merge-conflicts t))
(and (= ?U x_status) (= ?D y_status)) ((or (= (char-after) ?1)
(and (= ?U x_status) (= ?A y_status)) (= (char-after) ?2))
(and (= ?D x_status) (= ?U y_status))) (push (eshell-starship--git-interpret-file-status
(push ?= status-arr)) (char-after (+ 2 (point)))
((or (= x_status ?D) (= y_status ?D)) (char-after (+ 3 (point))))
(push ? status-arr)) status-chars)))
((or (= x_status ?R) (= y_status ?R)) (forward-line))
(push status-arr)) (concat (eshell-starship--git-interpret-branch-status (not (zerop ahead))
((= y_status ?M) (not (zerop behind)))
(push ?! status-arr)) (when merge-conflicts "=")
((or (= x_status ?A) (= x_status ?M)) (when stash "$")
(push ?+ status-arr)) (apply 'string (sort (seq-uniq status-chars) #'<)))))
((= x_status y_status ??)
(push ?? status-arr)))
finally
(sort status-arr #'(lambda (a b)
(cond
((= a ?=)
t)
((= b ?=)
nil)
(t
(< a b)))))
(when (eshell-starship--prompt-git-has-stash)
(if (= (car status-arr) ?=)
(setq status-arr (append '(?= ?$) (cdr status-arr)))
(push ?$ status-arr)))
(when-let (branch-status (eshell-starship--prompt-current-branch-status
first-line))
(push branch-status status-arr))
finally return (apply 'string (seq-uniq status-arr))))))
(defun eshell-starship--prompt-git-get-operation () (defun eshell-starship--git-current-operation ()
"Return the current git operation. For example, a revert." "Return the current git operation.
For example, a revert. If there is no current operation, return nil."
(let ((git-dir (expand-file-name ".git" (vc-git-root default-directory)))) (let ((git-dir (expand-file-name ".git" (vc-git-root default-directory))))
(cond (cond
((file-exists-p (expand-file-name "rebase-apply/applying" git-dir)) ((file-exists-p (expand-file-name "rebase-apply/applying" git-dir))
@ -140,36 +149,48 @@ STATUS-LINE is the first line of output from \"git status --porcelain=v1 -b\"."
((file-exists-p (expand-file-name "REVERT_HEAD" git-dir)) ((file-exists-p (expand-file-name "REVERT_HEAD" git-dir))
"REVERTING")))) "REVERTING"))))
(defun eshell-starship--prompt-git-status () (defun eshell-starship--git-status ()
"Get git status for `eshell-starship--prompt-function'." "Return the text for the git module for `eshell-starship--prompt-function'."
(let ((branch (car (vc-git-branches))) (with-temp-buffer
(state (eshell-starship--prompt-git-state-chars)) (when (zerop (vc-git-command t nil nil "status" "--porcelain=v2"
(operation (eshell-starship--prompt-git-get-operation))) "--branch" "--show-stash"))
(goto-char (point-min))
(cl-destructuring-bind (oid head upstream ahead behind stash)
(eshell-starship--git-parse-status-headers)
(let ((file-status (eshell-starship--git-file-status stash ahead
behind))
(operation (eshell-starship--git-current-operation)))
(concat (concat
(propertize (concat " 󰊢 " branch) 'face '(:foreground "medium purple")) (if (string= "(detached)" head)
(unless (string-empty-p state) (propertize (concat " (" (substring oid 0 7) ")")
(propertize (concat " [" state "]") 'face '(:foreground "red"))) 'face '(:foreground "lawn green"))
(propertize (concat " 󰊢 " head)
'face '(:foreground "medium purple")))
(unless (string-empty-p file-status)
(propertize (concat " [" file-status "]")
'face '(:foreground "red")))
(when operation (when operation
(concat " (" (propertize operation 'face (concat " (" (propertize
'(:inherit 'bold :foreground "yellow")) ")"))))) operation 'face
'(:inherit bold :foreground "yellow")) ")"))))))))
(defun eshell-starship--prompt-vc-status () (defun eshell-starship--vc-status ()
"Get vc status for `eshell-starship--prompt-function'." "Get vc status for `eshell-starship--prompt-function'."
(if-let (backend (vc-responsible-backend default-directory t)) (if-let (backend (vc-responsible-backend default-directory t))
(if (eq backend 'Git) (if (eq backend 'Git)
(eshell-starship--prompt-git-status) (eshell-starship--git-status)
(propertize (propertize
(concat "" (downcase (symbol-name backend))) (concat "" (downcase (symbol-name backend)))
'face '(:foreground "purple"))))) 'face '(:foreground "purple")))))
(defvar-local eshell-starship--prompt-last-start-time nil (defvar-local eshell-starship--last-start-time nil
"Start time of last eshell command.") "Start time of last eshell command.")
(defun eshell-starship--prompt-timer-pre-cmd () (defun eshell-starship--timer-pre-cmd ()
"Command run before each eshell program to record the time." "Command run before each eshell program to record the time."
(setq eshell-starship--prompt-last-start-time (current-time))) (setq eshell-starship--last-start-time (current-time)))
(add-hook 'eshell-pre-command-hook #'eshell-starship--prompt-timer-pre-cmd) (add-hook 'eshell-pre-command-hook #'eshell-starship--timer-pre-cmd)
(defun eshell-starship--prompt-format-span (span) (defun eshell-starship--prompt-format-span (span)
"Format SPAN as \"XhXms\"." "Format SPAN as \"XhXms\"."
@ -182,12 +203,12 @@ STATUS-LINE is the first line of output from \"git status --porcelain=v1 -b\"."
(format "%dm" mins)) (format "%dm" mins))
(format "%ds" secs)))) (format "%ds" secs))))
(defun eshell-starship--prompt-last-command-time (end-time) (defun eshell-starship--last-command-time (end-time)
"Return the prompt component for the time of the last command. "Return the prompt component for the time of the last command.
END-TIME is the time when the command finished executing." END-TIME is the time when the command finished executing."
(if-let ((eshell-starship--prompt-last-start-time) (if-let ((eshell-starship--last-start-time)
(len (time-subtract end-time (len (time-subtract end-time
eshell-starship--prompt-last-start-time)) eshell-starship--last-start-time))
(float-len (float-time len)) (float-len (float-time len))
((< 3 float-len)) ((< 3 float-len))
(int-len (round float-len))) (int-len (round float-len)))
@ -198,14 +219,14 @@ END-TIME is the time when the command finished executing."
(defun eshell-starship--prompt-function () (defun eshell-starship--prompt-function ()
"Function for `eshell-prompt-function'." "Function for `eshell-prompt-function'."
(let* ((end-time (current-time)) (let* ((end-time (current-time))
(dir (eshell-starship--prompt-get-dir)) (dir (eshell-starship--get-current-dir))
(prompt (concat (prompt (concat
"\n" "\n"
(propertize dir 'face '(:foreground "dark turquoise")) (propertize dir 'face '(:foreground "dark turquoise"))
(unless (file-writable-p dir) (unless (file-writable-p dir)
"") "")
(eshell-starship--prompt-vc-status) (eshell-starship--vc-status)
(eshell-starship--prompt-last-command-time end-time) (eshell-starship--last-command-time end-time)
(propertize "\n" 'read-only t 'rear-nonsticky t) (propertize "\n" 'read-only t 'rear-nonsticky t)
(propertize (propertize
" " 'face `(:foreground " " 'face `(:foreground
@ -213,7 +234,7 @@ END-TIME is the time when the command finished executing."
"lime green" "lime green"
"red")) "red"))
'rear-nonsticky t)))) 'rear-nonsticky t))))
(setq eshell-starship--prompt-last-start-time nil) (setq eshell-starship--last-start-time nil)
prompt)) prompt))
(defvar-local ehsell-starship--restore-state nil (defvar-local ehsell-starship--restore-state nil