Finish eshell-starship.el refactor
This commit is contained in:
parent
7310a981e7
commit
5c74237b4f
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user