2024-01-18 23:37:05 -08:00
|
|
|
|
;;; eshell-starship.el --- Starship-like (https://starship.rs) prompt for eshell -*- lexical-binding: t; -*-
|
2024-01-17 04:16:49 -08:00
|
|
|
|
;;; Commentary:
|
|
|
|
|
;;; Code:
|
|
|
|
|
(require 'vc)
|
|
|
|
|
(require 'vc-git)
|
|
|
|
|
(require 'eshell)
|
2024-01-17 21:12:42 -08:00
|
|
|
|
(require 'cl-lib)
|
2024-01-17 04:16:49 -08:00
|
|
|
|
|
2024-12-21 21:38:15 -08:00
|
|
|
|
;;; Configuration options
|
|
|
|
|
(defgroup eshell-starship nil
|
|
|
|
|
"Starship-like (starship.rs) prompt for `eshell'."
|
|
|
|
|
:version "0.0.1"
|
|
|
|
|
:prefix 'eshell-starship-
|
|
|
|
|
:group 'eshell)
|
|
|
|
|
|
|
|
|
|
(defvar-local eshell-starship--current-explain-buffer nil
|
|
|
|
|
"The eshell-starship explain buffer for this eshell buffer.")
|
|
|
|
|
|
|
|
|
|
(defcustom eshell-starship-explain-auto-update t
|
|
|
|
|
"Non-nil if eshell-starship explain buffers shoul auto-update."
|
|
|
|
|
:group 'eshell-starship
|
|
|
|
|
:tag "Auto-update explain buffers"
|
|
|
|
|
:type 'boolean)
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--defcustom-setter (sym val)
|
|
|
|
|
"Set SYM to VAL (using `set-default-toplevel-value').
|
|
|
|
|
This will also update all eshell-starship explain buffers that need updating."
|
|
|
|
|
(set-default-toplevel-value sym val)
|
|
|
|
|
(dolist (buffer (buffer-list))
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(when (and (derived-mode-p 'eshell-mode)
|
|
|
|
|
(buffer-live-p eshell-starship--current-explain-buffer))
|
|
|
|
|
(with-current-buffer eshell-starship--current-explain-buffer
|
|
|
|
|
(when eshell-starship-explain-auto-update
|
|
|
|
|
(revert-buffer)))))))
|
|
|
|
|
|
|
|
|
|
(defcustom eshell-starship-module-order
|
|
|
|
|
'("remote" "cwd" "vc" t "cmd-time" "arrow")
|
|
|
|
|
"The order of modules for eshell-starship.
|
|
|
|
|
This is a list with each element being a module name. The special value t can
|
|
|
|
|
appear at most once to denote \"all remaining modules\"."
|
|
|
|
|
:group 'eshell-starship
|
|
|
|
|
:tag "Module order"
|
|
|
|
|
:type '(repeat (choice (const :tag "Remaining modules" t)
|
|
|
|
|
(string :tag "Module")))
|
|
|
|
|
:set 'eshell-starship--defcustom-setter)
|
|
|
|
|
|
|
|
|
|
(defcustom eshell-starship-disabled-modules '()
|
|
|
|
|
"List of disabled eshell-starship modules."
|
|
|
|
|
:group 'eshell-starship
|
|
|
|
|
:tag "Disabled modules"
|
|
|
|
|
:type '(repeat (string :tag "Module"))
|
|
|
|
|
:set 'eshell-starship--defcustom-setter)
|
|
|
|
|
|
|
|
|
|
(defcustom eshell-starship-explain-suppress-refresh-messages nil
|
|
|
|
|
"Weather to suppress messages during eshell-starship explore refreshes."
|
|
|
|
|
:group 'eshell-starship
|
|
|
|
|
:tag "Suppress eshell-starship explore refresh messages"
|
|
|
|
|
:type 'boolean)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Module API
|
|
|
|
|
(defvar eshell-starship-modules (make-hash-table :test 'equal)
|
|
|
|
|
"List of modules used by eshell-starship.")
|
2024-01-31 00:50:08 -08:00
|
|
|
|
|
2024-12-20 20:22:23 -08:00
|
|
|
|
(defvar-local eshell-starship--module-cache nil
|
2024-12-21 21:38:15 -08:00
|
|
|
|
"Hash table mapping modules to a list of their last output.
|
|
|
|
|
The entries of this hash table are of the the form (VALID OUTPUT LAST-TIMES).
|
|
|
|
|
LAST-TIMES is a list of the 10 last execution times for this module.")
|
|
|
|
|
|
|
|
|
|
(defclass eshell-starship-module ()
|
|
|
|
|
((name :initarg :name
|
|
|
|
|
:accessor eshell-starship-module-name
|
|
|
|
|
:type string
|
|
|
|
|
:documentation "The name of this module.")
|
|
|
|
|
(precmd-action :initarg :precmd-action
|
|
|
|
|
:initform nil
|
|
|
|
|
:accessor eshell-starship-module-precmd-action
|
|
|
|
|
:type (or function null)
|
|
|
|
|
:documentation
|
|
|
|
|
"A function to run before each command is run.")
|
|
|
|
|
(postcmd-action :initarg :postcmd-action
|
|
|
|
|
:initform nil
|
|
|
|
|
:accessor eshell-starship-module-postcmd-action
|
|
|
|
|
:type (or function null)
|
|
|
|
|
:documentation
|
|
|
|
|
"A function to run after each command is run.")
|
|
|
|
|
(predicate :initarg :predicate
|
|
|
|
|
:initform 'ignore
|
|
|
|
|
:accessor eshell-starship-module-predicate
|
|
|
|
|
:type function
|
|
|
|
|
:documentation
|
|
|
|
|
"A function that should return non-nil if the module should be run.")
|
|
|
|
|
(files :initarg :files
|
|
|
|
|
:initform nil
|
|
|
|
|
:accessor eshell-starship-module-files
|
|
|
|
|
:type list
|
|
|
|
|
:documentation
|
|
|
|
|
"A list of files that indicate that the module should be run.")
|
|
|
|
|
(dirs :initarg :dirs
|
|
|
|
|
:initform nil
|
|
|
|
|
:accessor eshell-starship--module-dirs
|
|
|
|
|
:type list
|
|
|
|
|
:documentation
|
|
|
|
|
"A list of directories that indicate that the module should be run.")
|
|
|
|
|
(extensions :initarg :extensions
|
|
|
|
|
:initform nil
|
|
|
|
|
:accessor eshell-starship-module-extensions
|
|
|
|
|
:type list
|
|
|
|
|
:documentation
|
|
|
|
|
"A list of extensions that indicate that the module should be run.")
|
|
|
|
|
(prefix :initarg :prefix
|
|
|
|
|
:initform ""
|
|
|
|
|
:accessor eshell-starship-module-prefix
|
|
|
|
|
:type string
|
|
|
|
|
:documentation "Text to be placed before the module's icon. This is
|
|
|
|
|
not colored.")
|
|
|
|
|
(icon :initarg :icon
|
|
|
|
|
:initform ""
|
|
|
|
|
:accessor eshell-starship-module-icon
|
|
|
|
|
:type string
|
|
|
|
|
:documentation "The modules icon. This is colored.")
|
|
|
|
|
(postfix :initarg :postfix
|
|
|
|
|
:initform ""
|
|
|
|
|
:accessor eshell-starship-module-prefix
|
|
|
|
|
:type string
|
|
|
|
|
:documentation "Text to be placed after the module's content. This is
|
|
|
|
|
not colored.")
|
|
|
|
|
(color :initarg :color
|
|
|
|
|
:initform nil
|
|
|
|
|
:accessor eshell-starship-module-color
|
|
|
|
|
:type (or null string)
|
|
|
|
|
:documentation "The color to give the module's icon and main text.
|
|
|
|
|
Use `list-colors-display' to get a list of some possible values. This can also
|
|
|
|
|
be nil.")
|
|
|
|
|
(allow-remote :initarg :allow-remote
|
|
|
|
|
:initform t
|
|
|
|
|
:accessor eshell-starship-module-allow-remote-p
|
|
|
|
|
:type boolean
|
|
|
|
|
:documentation "Weather the module should be run if
|
|
|
|
|
`default-directory' is a `file-remote-p'.")
|
|
|
|
|
(action :initarg :action
|
|
|
|
|
:initform 'ignore
|
|
|
|
|
:accessor eshell-starship-module-action
|
|
|
|
|
:type function
|
|
|
|
|
:documentation "A function that produces the main text for the
|
|
|
|
|
module.")
|
|
|
|
|
(reload-on :initarg :reload-on
|
|
|
|
|
:initform 'never
|
|
|
|
|
:accessor eshell-starship-module-reload-on
|
|
|
|
|
:type (or (member never always cwd)
|
|
|
|
|
(list-of (member never always cwd)))
|
|
|
|
|
:documentation "A list of times when this module should be
|
|
|
|
|
reloaded. Current possible values are:
|
|
|
|
|
- never (or an empty list): don't ever re-run this module
|
|
|
|
|
- always: re-run this module every time the prompt is updated
|
|
|
|
|
- cwd: re-run this module when the CWD changes")
|
|
|
|
|
(doc :initarg :doc
|
|
|
|
|
:initform "No documentation provided."
|
|
|
|
|
:accessor eshell-starship-module-doc
|
|
|
|
|
:type string
|
|
|
|
|
:documentation "The documentation for this module."))
|
|
|
|
|
(:documentation "Class for eshell-starship modules."))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--defmodule-real (name &rest opts)
|
|
|
|
|
"Do the work of `eshell-starship-defmodule'.
|
|
|
|
|
NAME is the module name (a symbol) and OPTS is the list of options to pass to
|
|
|
|
|
the module's constructor."
|
|
|
|
|
(let ((module (apply 'make-instance 'eshell-starship-module
|
|
|
|
|
:name (symbol-name name) opts)))
|
|
|
|
|
(dolist (buffer (buffer-list))
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(when (hash-table-p eshell-starship--module-cache)
|
|
|
|
|
(remhash (symbol-name name) eshell-starship--module-cache))))
|
|
|
|
|
;; This returns module
|
|
|
|
|
(puthash (symbol-name name) module eshell-starship-modules)))
|
|
|
|
|
|
|
|
|
|
(defmacro eshell-starship-defmodule (name &rest opts)
|
|
|
|
|
"Create a new eshell-starship named NAME module with OPTS."
|
2024-01-31 00:50:08 -08:00
|
|
|
|
(declare (indent defun))
|
2024-12-21 21:38:15 -08:00
|
|
|
|
`(eshell-starship--defmodule-real ',name ,@opts))
|
2024-01-31 00:50:08 -08:00
|
|
|
|
|
2024-12-21 21:38:15 -08:00
|
|
|
|
|
|
|
|
|
;;; Utility functions
|
2024-01-31 00:50:08 -08:00
|
|
|
|
(cl-defmacro eshell-starship-find-version-function (command pattern
|
2024-12-21 21:38:15 -08:00
|
|
|
|
&rest format)
|
|
|
|
|
"Return a version finding function for COMMAND.
|
2024-01-31 00:50:08 -08:00
|
|
|
|
COMMAND is in the form of (exec args...). The temp buffer that was used to run
|
2024-12-21 21:38:15 -08:00
|
|
|
|
COMMAND will then have `re-search-forward' run with PATTERN. FORMAT will then
|
|
|
|
|
be passed verbatim as the arguments to `concat'."
|
2024-01-31 00:50:08 -08:00
|
|
|
|
(declare (indent defun))
|
|
|
|
|
`(lambda ()
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(when (zerop (process-file ,(car command) nil t nil ,@(cdr command)))
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(when (re-search-forward ,pattern nil t)
|
|
|
|
|
(concat ,@format))))))
|
|
|
|
|
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(cl-defun eshell-starship-format-span (span &optional (places 0))
|
|
|
|
|
"Format SPAN (in seconds) as \"XhXmXs\".
|
|
|
|
|
The number is rounded to PLACES before being rendered."
|
|
|
|
|
(let* ((ispan (round span))
|
|
|
|
|
(hours (/ ispan 3600))
|
|
|
|
|
(mins (% (/ ispan 60) 60))
|
|
|
|
|
(secs (mod span 60)))
|
|
|
|
|
(concat (when (/= hours 0)
|
|
|
|
|
(format "%dh" hours))
|
|
|
|
|
(when (or (/= mins 0) (/= hours 0))
|
|
|
|
|
(format "%dm" mins))
|
|
|
|
|
(format (format "%%.%dfs" places) secs))))
|
2024-01-30 16:17:23 -08:00
|
|
|
|
|
2024-12-21 21:38:15 -08:00
|
|
|
|
|
|
|
|
|
;;; CWD Module
|
2024-01-17 04:16:49 -08:00
|
|
|
|
(defun eshell-starship--replace-home-with-tilda (path)
|
|
|
|
|
"If PATH beings with $HOME (the environment variable), replace it with ~."
|
|
|
|
|
(let ((home (getenv "HOME")))
|
|
|
|
|
(if (equal home path)
|
|
|
|
|
"~"
|
|
|
|
|
(setq home (file-name-as-directory home))
|
|
|
|
|
(if (string-prefix-p home path)
|
|
|
|
|
(concat "~/" (seq-subseq path (length home)))
|
|
|
|
|
path))))
|
|
|
|
|
|
2024-01-18 16:11:13 -08:00
|
|
|
|
(defun eshell-starship--limit-path-parts (num path)
|
2024-01-17 04:16:49 -08:00
|
|
|
|
"Cut PATH down to NUM components.
|
|
|
|
|
Example:
|
|
|
|
|
/this/is/a/path 3-> is/a/path"
|
|
|
|
|
(let ((parts (string-split path "/" t nil)))
|
|
|
|
|
(concat
|
|
|
|
|
(when (and (file-name-absolute-p path)
|
|
|
|
|
(not (equal "~" (car parts)))
|
|
|
|
|
(<= (length parts) num))
|
|
|
|
|
"/")
|
|
|
|
|
(string-join (last parts num) "/"))))
|
|
|
|
|
|
2024-01-18 16:11:13 -08:00
|
|
|
|
(defun eshell-starship--get-current-dir ()
|
2024-01-17 04:16:49 -08:00
|
|
|
|
"Get dir for `eshell-starship--prompt-function'."
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(concat
|
|
|
|
|
(propertize (eshell-starship--limit-path-parts
|
|
|
|
|
3 (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)))
|
|
|
|
|
'face '(:foreground "dark turquoise"))
|
|
|
|
|
(unless (file-writable-p default-directory)
|
|
|
|
|
" ")))
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule cwd
|
|
|
|
|
:predicate 'always
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action 'eshell-starship--get-current-dir
|
|
|
|
|
:doc "The current working directory.")
|
2024-01-18 16:11:13 -08:00
|
|
|
|
|
2024-12-21 21:38:15 -08:00
|
|
|
|
|
|
|
|
|
;;; VC (git) module
|
2024-01-18 16:11:13 -08:00
|
|
|
|
(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)
|
2024-01-17 21:12:42 -08:00
|
|
|
|
(cond
|
2024-01-18 16:11:13 -08:00
|
|
|
|
((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)))
|
2024-01-17 21:12:42 -08:00
|
|
|
|
|
2024-01-18 16:11:13 -08:00
|
|
|
|
(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))
|
|
|
|
|
?+)))
|
2024-01-17 04:16:49 -08:00
|
|
|
|
|
2024-01-18 16:11:13 -08:00
|
|
|
|
(defun eshell-starship--git-interpret-branch-status (ahead behind)
|
|
|
|
|
"Get the status char for the current branch and its remote.
|
|
|
|
|
AHEAD should evaluate to t if the current branch is ahead of its remote, and
|
|
|
|
|
BEHIND should evaluate to t if the current branch is behind its remote."
|
|
|
|
|
(cond
|
|
|
|
|
((and ahead behind) "")
|
|
|
|
|
(ahead "")
|
|
|
|
|
(behind "")))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--git-file-status (stash ahead behind)
|
|
|
|
|
"Get the file status string for the git prompt module.
|
|
|
|
|
STASH should be t if there is current stashed data stash. AHEAD and BEHIND
|
|
|
|
|
should be as for `eshell-starship--git-interpret-branch-status'."
|
|
|
|
|
(let ((merge-conflicts nil)
|
|
|
|
|
(status-chars nil))
|
2024-01-18 23:37:05 -08:00
|
|
|
|
(while (not (eobp))
|
2024-01-18 16:11:13 -08:00
|
|
|
|
(cond
|
|
|
|
|
((= (char-after) ??)
|
|
|
|
|
(push ?? status-chars))
|
|
|
|
|
((= (char-after) ?u)
|
|
|
|
|
(setq merge-conflicts t))
|
|
|
|
|
((or (= (char-after) ?1)
|
|
|
|
|
(= (char-after) ?2))
|
|
|
|
|
(push (eshell-starship--git-interpret-file-status
|
|
|
|
|
(char-after (+ 2 (point)))
|
|
|
|
|
(char-after (+ 3 (point))))
|
|
|
|
|
status-chars)))
|
|
|
|
|
(forward-line))
|
|
|
|
|
(concat (eshell-starship--git-interpret-branch-status (not (zerop ahead))
|
|
|
|
|
(not (zerop behind)))
|
|
|
|
|
(when merge-conflicts "=")
|
|
|
|
|
(when stash "$")
|
|
|
|
|
(apply 'string (sort (seq-uniq status-chars) #'<)))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--git-current-operation ()
|
|
|
|
|
"Return the current git operation.
|
|
|
|
|
For example, a revert. If there is no current operation, return nil."
|
2024-01-17 04:16:49 -08:00
|
|
|
|
(let ((git-dir (expand-file-name ".git" (vc-git-root default-directory))))
|
|
|
|
|
(cond
|
2024-01-18 03:14:00 -08:00
|
|
|
|
((file-exists-p (expand-file-name "rebase-apply/applying" git-dir))
|
|
|
|
|
"AM")
|
|
|
|
|
((file-exists-p (expand-file-name "rebase-apply/rebasing" git-dir))
|
|
|
|
|
"REBASE")
|
|
|
|
|
((file-exists-p (expand-file-name "rebase-apply" git-dir))
|
|
|
|
|
"AM/REBASE")
|
2024-01-17 04:16:49 -08:00
|
|
|
|
((file-exists-p (expand-file-name "rebase-merge" git-dir))
|
|
|
|
|
"REBASING")
|
2024-01-18 03:14:00 -08:00
|
|
|
|
((file-exists-p (expand-file-name "CHERRY_PICK_HEAD" git-dir))
|
|
|
|
|
"CHERRY-PICKING")
|
2024-01-17 04:16:49 -08:00
|
|
|
|
((file-exists-p (expand-file-name "MERGE_HEAD" git-dir))
|
2024-01-18 03:14:00 -08:00
|
|
|
|
"MERGING")
|
|
|
|
|
((file-exists-p (expand-file-name "BISECT_LOG" git-dir))
|
|
|
|
|
"BISECTING")
|
|
|
|
|
((file-exists-p (expand-file-name "REVERT_HEAD" git-dir))
|
|
|
|
|
"REVERTING"))))
|
2024-01-17 04:16:49 -08:00
|
|
|
|
|
2024-01-18 16:11:13 -08:00
|
|
|
|
(defun eshell-starship--git-status ()
|
|
|
|
|
"Return the text for the git module for `eshell-starship--prompt-function'."
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(when (zerop (vc-git-command t nil nil "status" "--porcelain=v2"
|
|
|
|
|
"--branch" "--show-stash"))
|
|
|
|
|
(goto-char (point-min))
|
2024-01-31 00:50:08 -08:00
|
|
|
|
(cl-destructuring-bind (oid head _upstream ahead behind stash)
|
2024-01-18 16:11:13 -08:00
|
|
|
|
(eshell-starship--git-parse-status-headers)
|
|
|
|
|
(let ((file-status (eshell-starship--git-file-status stash ahead
|
|
|
|
|
behind))
|
|
|
|
|
(operation (eshell-starship--git-current-operation)))
|
|
|
|
|
(concat
|
|
|
|
|
(if (string= "(detached)" head)
|
|
|
|
|
(propertize (concat " (" (substring oid 0 7) ")")
|
|
|
|
|
'face '(:foreground "lawn green"))
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(propertize (concat " " head)
|
2024-01-18 16:11:13 -08:00
|
|
|
|
'face '(:foreground "medium purple")))
|
|
|
|
|
(unless (string-empty-p file-status)
|
|
|
|
|
(propertize (concat " [" file-status "]")
|
|
|
|
|
'face '(:foreground "red")))
|
|
|
|
|
(when operation
|
|
|
|
|
(concat " (" (propertize
|
|
|
|
|
operation 'face
|
|
|
|
|
'(:inherit bold :foreground "yellow")) ")"))))))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--vc-status ()
|
2024-01-17 04:16:49 -08:00
|
|
|
|
"Get vc status for `eshell-starship--prompt-function'."
|
|
|
|
|
(if-let (backend (vc-responsible-backend default-directory t))
|
|
|
|
|
(if (eq backend 'Git)
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(let ((status (eshell-starship--git-status)))
|
|
|
|
|
(and (not (zerop (length status))) status))
|
2024-01-17 04:16:49 -08:00
|
|
|
|
(propertize
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(concat " " (downcase (symbol-name backend)))
|
2024-01-17 04:16:49 -08:00
|
|
|
|
'face '(:foreground "purple")))))
|
|
|
|
|
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(eshell-starship-defmodule vc
|
|
|
|
|
:predicate 'always
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'always
|
|
|
|
|
:action 'eshell-starship--vc-status
|
|
|
|
|
:doc "The working directory's version control status.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Timer module
|
2024-01-18 16:11:13 -08:00
|
|
|
|
(defvar-local eshell-starship--last-start-time nil
|
2024-01-17 04:16:49 -08:00
|
|
|
|
"Start time of last eshell command.")
|
|
|
|
|
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(defvar-local eshell-starship--last-end-time nil
|
|
|
|
|
"End time of last eshell command.")
|
2024-01-17 04:16:49 -08:00
|
|
|
|
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(defun eshell-starship--last-command-time ()
|
|
|
|
|
"Return the prompt component for the time of the last command."
|
|
|
|
|
(prog1
|
|
|
|
|
(and eshell-starship--last-start-time
|
|
|
|
|
eshell-starship--last-end-time
|
|
|
|
|
(eshell-starship-format-span (- eshell-starship--last-end-time
|
|
|
|
|
eshell-starship--last-start-time)))
|
|
|
|
|
(setq eshell-starship--last-start-time nil)))
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule cmd-time
|
|
|
|
|
:prefix "took "
|
|
|
|
|
:color "gold1"
|
|
|
|
|
:reload-on 'always
|
|
|
|
|
:precmd-action (lambda ()
|
|
|
|
|
(setq eshell-starship--last-start-time (float-time)))
|
|
|
|
|
:postcmd-action (lambda ()
|
|
|
|
|
(setq eshell-starship--last-end-time (float-time)))
|
|
|
|
|
:predicate (lambda ()
|
|
|
|
|
(and eshell-starship--last-start-time
|
|
|
|
|
eshell-starship--last-end-time
|
|
|
|
|
(<= 3 (- eshell-starship--last-end-time
|
|
|
|
|
eshell-starship--last-start-time))))
|
|
|
|
|
:action 'eshell-starship--last-command-time
|
|
|
|
|
:doc "The amount of time it took the last command to execute.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Language modules
|
|
|
|
|
(eshell-starship-defmodule cc
|
|
|
|
|
:extensions '("c" "h")
|
|
|
|
|
:prefix "via "
|
|
|
|
|
:icon "C "
|
|
|
|
|
:color "spring green"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action (eshell-starship-find-version-function
|
|
|
|
|
("cc" "-v")
|
|
|
|
|
"^\\([-a-zA-Z]+\\) version \\([0-9]+\\.[0-9]+\\.[0-9]+\\)"
|
|
|
|
|
"v" (match-string 2) "-" (match-string 1))
|
|
|
|
|
:doc "Your C compiler version.")
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule c++
|
|
|
|
|
:extensions '("cpp" "cc" "cxx" "hpp" "hh" "hxx")
|
|
|
|
|
:prefix "via "
|
|
|
|
|
:icon " "
|
|
|
|
|
:color "royal blue"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action (eshell-starship-find-version-function
|
|
|
|
|
("cpp" "--version")
|
|
|
|
|
"\\(GCC\\|clang\\).+?\\([0-9]+\\.[0-9]+\\.[0-9]+\\)"
|
|
|
|
|
"v" (match-string 2) "-" (downcase (match-string 1)))
|
|
|
|
|
:doc "Your C++ compiler version.")
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule rust
|
|
|
|
|
:extensions '("rs")
|
|
|
|
|
:files '("Cargo.toml")
|
|
|
|
|
:prefix "via "
|
|
|
|
|
:icon "🦀 "
|
|
|
|
|
:color "red"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action (eshell-starship-find-version-function
|
|
|
|
|
("rustc" "--version")
|
|
|
|
|
"^rustc \\([0-9]+\\.[0-9]+\\.[0-9]+\\)"
|
|
|
|
|
"v" (match-string 1))
|
|
|
|
|
:doc "Your Rust compiler version.")
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule cmake
|
|
|
|
|
:files '("CMakeLists.txt" "CMakeCache.txt")
|
|
|
|
|
:prefix "via "
|
|
|
|
|
:icon " "
|
|
|
|
|
:color "blue"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action (eshell-starship-find-version-function
|
|
|
|
|
("cmake" "--version")
|
|
|
|
|
"cmake version \\([0-9]+\\.[0-9]+\\.[0-9]+\\)"
|
|
|
|
|
"v" (match-string 1))
|
|
|
|
|
:doc "Your CMake version.")
|
|
|
|
|
|
|
|
|
|
(require 'inf-lisp nil t)
|
|
|
|
|
(when (featurep 'inf-lisp)
|
|
|
|
|
(eshell-starship-defmodule common-lisp
|
|
|
|
|
:extensions '("asd" "lisp")
|
|
|
|
|
:prefix "via "
|
|
|
|
|
:icon " "
|
|
|
|
|
:color "green yellow"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action (eshell-starship-find-version-function
|
|
|
|
|
(inferior-lisp-program "--version")
|
|
|
|
|
"[a-zA-Z]+ [0-9.]+"
|
|
|
|
|
(match-string 0))
|
|
|
|
|
:doc "Your current inferior-lisp program."))
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule elisp
|
|
|
|
|
:extensions '("el" "elc" "eln")
|
|
|
|
|
:prefix "via "
|
|
|
|
|
:icon " "
|
|
|
|
|
:color "dark orchid"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'never
|
|
|
|
|
:action (lambda ()
|
|
|
|
|
emacs-version)
|
|
|
|
|
:doc "The current emacs-version.")
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule java
|
|
|
|
|
:extensions '("java" "class" "gradle" "jar" "clj" "cljc")
|
|
|
|
|
:files '("pom.xml" "build.gradle.kts" "build.sbt" ".java-version" "deps.edn"
|
|
|
|
|
"project.clj" "build.boot" ".sdkmanrc")
|
|
|
|
|
:prefix "via "
|
|
|
|
|
:icon "☕ "
|
|
|
|
|
:color "dark red"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action (eshell-starship-find-version-function
|
|
|
|
|
("java" "-version")
|
|
|
|
|
"version \"\\([0-9]+\\)\""
|
|
|
|
|
"v" (match-string 1))
|
|
|
|
|
:doc "Your Java version.")
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule zig
|
|
|
|
|
:extensions '("zig")
|
|
|
|
|
:prefix "via "
|
|
|
|
|
:icon "↯ "
|
|
|
|
|
:color "yellow"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action (eshell-starship-find-version-function
|
|
|
|
|
("zig" "version")
|
|
|
|
|
".+"
|
|
|
|
|
"v" (match-string 0))
|
|
|
|
|
:doc "Your Zig version.")
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule python
|
|
|
|
|
:extensions '("py" "ipynb")
|
|
|
|
|
:files '(".python-version" "Pipfile" "__init__.py" "pyproject.toml"
|
|
|
|
|
"requirements.txt" "setup.py" "tox.ini" "pixi.toml")
|
|
|
|
|
:prefix "via "
|
|
|
|
|
:icon "🐍 "
|
|
|
|
|
:color "#CECB00"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action (eshell-starship-find-version-function
|
|
|
|
|
("python" "--version")
|
|
|
|
|
"^Python \\([0-9.]+\\)"
|
|
|
|
|
"v" (match-string 1))
|
|
|
|
|
:doc "Your current system-wide Python version.")
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule php
|
|
|
|
|
:extensions '("php")'
|
|
|
|
|
:files '("composer.json" ".php-version")
|
|
|
|
|
:prefix "via "
|
|
|
|
|
:icon "🐘 "
|
|
|
|
|
:color "#AFAFFF"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action (eshell-starship-find-version-function
|
|
|
|
|
("php" "--version")
|
|
|
|
|
"^PHP \\([0-9.]+\\)"
|
|
|
|
|
"v" (match-string 1))
|
|
|
|
|
:doc "Your current PHP version.")
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule node
|
|
|
|
|
:extensions '("js" "mjs" "cjs" "ts" "mts" "cts")
|
|
|
|
|
:files '("package.json" ".node-version" ".nvmrc")
|
|
|
|
|
:dirs '("node_modules")
|
|
|
|
|
:icon " "
|
|
|
|
|
:color "green"
|
|
|
|
|
:allow-remote nil
|
|
|
|
|
:reload-on 'cwd
|
|
|
|
|
:action (eshell-starship-find-version-function
|
|
|
|
|
("node" "--version")
|
|
|
|
|
".+" (match-string 0))
|
|
|
|
|
:doc "Your current NodeJS version.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Misc modules
|
|
|
|
|
(eshell-starship-defmodule remote
|
|
|
|
|
:icon "🌐"
|
|
|
|
|
:color "light blue"
|
|
|
|
|
:predicate (lambda ()
|
|
|
|
|
(file-remote-p default-directory))
|
|
|
|
|
:doc "A small icon if the working directory is remote.")
|
|
|
|
|
|
|
|
|
|
(eshell-starship-defmodule arrow
|
|
|
|
|
:predicate 'always
|
|
|
|
|
:reload-on 'always
|
|
|
|
|
:action (lambda ()
|
|
|
|
|
(concat
|
|
|
|
|
(propertize "\n" 'read-only t 'rear-nonsticky t)
|
|
|
|
|
(propertize
|
|
|
|
|
"❯ " 'face `(:foreground
|
|
|
|
|
,(if (= eshell-last-command-status 0)
|
|
|
|
|
"lime green"
|
|
|
|
|
"red"))
|
|
|
|
|
'rear-nonsticky t)))
|
|
|
|
|
:doc "An arrow that appears next to where you type.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Driver code
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship-clear-cache (&rest flags)
|
|
|
|
|
"Clear each cache entry with a \\=:reload-on of FLAGS.
|
|
|
|
|
If any of flags is t, clear all caches."
|
|
|
|
|
(interactive '(t) eshell-starship)
|
|
|
|
|
(cl-loop with force-clear = (member t flags)
|
|
|
|
|
for module being the hash-values of eshell-starship-modules
|
|
|
|
|
do (with-slots (name reload-on) module
|
|
|
|
|
(when-let (((or force-clear
|
|
|
|
|
(cl-intersection (ensure-list reload-on) flags)))
|
|
|
|
|
(cur-entry (gethash name
|
|
|
|
|
eshell-starship--module-cache)))
|
|
|
|
|
(setf (car cur-entry) nil)))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--cwd-clear-caches ()
|
|
|
|
|
"Clear caches that should be cleared on cwd for eshell-starship."
|
|
|
|
|
(eshell-starship-clear-cache 'cwd))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--exts-exist-p (&rest exts)
|
|
|
|
|
"Test if any files with EXTS at the end of their name exist.
|
|
|
|
|
The test is performed relative to `default-directory'."
|
|
|
|
|
(catch 'found
|
|
|
|
|
(dolist (ext exts)
|
|
|
|
|
(when (seq-filter #'(lambda (name)
|
|
|
|
|
(not (string-prefix-p "." name)))
|
|
|
|
|
(file-expand-wildcards (concat "*." ext)))
|
|
|
|
|
(throw 'found t)))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--files-exist-p (&rest names)
|
|
|
|
|
"Test if any of NAMES exist and are normal files.
|
|
|
|
|
The test is performed relative to `default-directory'."
|
|
|
|
|
(catch 'found
|
|
|
|
|
(dolist (name names)
|
|
|
|
|
(when (file-exists-p name)
|
|
|
|
|
(throw 'found t)))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--dirs-exist-p (&rest names)
|
|
|
|
|
"Test if any of NAMES exist and are directories.
|
|
|
|
|
The test is performed relative to `default-directory'."
|
|
|
|
|
(catch 'found
|
|
|
|
|
(dolist (name names)
|
|
|
|
|
(when (file-directory-p name)
|
|
|
|
|
(throw 'found t)))))
|
|
|
|
|
|
|
|
|
|
(cl-defun eshell-starship--display-module-p (module &optional
|
|
|
|
|
(dir default-directory))
|
|
|
|
|
"Return non-nil if MODULE should be displayed while in DIR."
|
|
|
|
|
(with-slots (name predicate files dirs extensions allow-remote) module
|
|
|
|
|
(and (not (cl-member name eshell-starship-disabled-modules :test 'equal))
|
|
|
|
|
(or allow-remote (not (file-remote-p dir)))
|
|
|
|
|
(let ((default-directory dir))
|
|
|
|
|
(or (and files (apply 'eshell-starship--files-exist-p files))
|
|
|
|
|
(and dirs (apply' eshell-starship--dirs-exist-p dirs))
|
|
|
|
|
(and extensions (apply' eshell-starship--exts-exist-p extensions))
|
|
|
|
|
(and predicate (funcall predicate)))))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--execute-module (module)
|
|
|
|
|
"Run the module MODULE and return its output.
|
|
|
|
|
Also cache the time it took to run it and its output."
|
|
|
|
|
(with-slots (name action prefix postfix icon color) module
|
|
|
|
|
(let ((oldtimes (cl-third (gethash name eshell-starship--module-cache)))
|
|
|
|
|
start-time result end-time)
|
|
|
|
|
(setq start-time (float-time)
|
|
|
|
|
result (funcall action)
|
|
|
|
|
end-time (float-time))
|
|
|
|
|
(when-let ((result)
|
|
|
|
|
(output (concat prefix
|
|
|
|
|
(if color
|
|
|
|
|
(propertize (concat icon result)
|
|
|
|
|
'face `(:foreground ,color))
|
|
|
|
|
(concat icon result))
|
|
|
|
|
postfix)))
|
|
|
|
|
(puthash name (list t output (cons (- end-time start-time)
|
|
|
|
|
(take 9 oldtimes)))
|
|
|
|
|
eshell-starship--module-cache)
|
|
|
|
|
output))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--execute-modules ()
|
|
|
|
|
"Execute all the modules in `eshell-starship-modules'.
|
|
|
|
|
Return a hash table mapping module names to their output."
|
|
|
|
|
(cl-loop
|
|
|
|
|
with output = (make-hash-table :test 'equal)
|
|
|
|
|
for module being the hash-values of eshell-starship-modules
|
|
|
|
|
for name = (eshell-starship-module-name module)
|
|
|
|
|
for reload-on = (ensure-list (eshell-starship-module-reload-on module))
|
|
|
|
|
for cache-entry = (gethash name eshell-starship--module-cache)
|
|
|
|
|
do (when (eshell-starship--display-module-p module)
|
|
|
|
|
(if (and (not (member 'always reload-on)) (car cache-entry))
|
|
|
|
|
(puthash name (cl-second cache-entry) output)
|
|
|
|
|
(puthash name (eshell-starship--execute-module module) output)))
|
|
|
|
|
finally (maphash (lambda (k v)
|
|
|
|
|
(unless v
|
|
|
|
|
(remhash k output)))
|
|
|
|
|
output)
|
|
|
|
|
finally return output))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--run-module-precmd-actions ()
|
|
|
|
|
"Run the pre-command action for each module."
|
|
|
|
|
(cl-loop for module being the hash-values of eshell-starship-modules
|
|
|
|
|
for precmd-action = (eshell-starship-module-precmd-action module)
|
|
|
|
|
when precmd-action
|
|
|
|
|
do (funcall precmd-action)))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--run-module-postcmd-actions ()
|
|
|
|
|
"Run the post-command action for each module."
|
|
|
|
|
(cl-loop for module being the hash-values of eshell-starship-modules
|
|
|
|
|
for postcmd-action = (eshell-starship-module-postcmd-action module)
|
|
|
|
|
when postcmd-action
|
|
|
|
|
do (funcall postcmd-action)))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--build-module-string ()
|
|
|
|
|
"Build a space-separated string of module outputs."
|
|
|
|
|
(let ((output (eshell-starship--execute-modules))
|
|
|
|
|
pre post found-rest)
|
|
|
|
|
(dolist (cur-name (mapcar (lambda (name)
|
|
|
|
|
(if (and (not (eq name t)) (symbolp name))
|
|
|
|
|
(symbol-name name)
|
|
|
|
|
name))
|
|
|
|
|
eshell-starship-module-order))
|
|
|
|
|
(cond
|
|
|
|
|
((and (eq cur-name t) found-rest)
|
|
|
|
|
(warn "t appears more than once in `eshell-starship-module-order"))
|
|
|
|
|
((eq cur-name t)
|
|
|
|
|
(setq found-rest t))
|
|
|
|
|
((not (gethash cur-name output))
|
|
|
|
|
;; skip
|
|
|
|
|
)
|
|
|
|
|
(found-rest
|
|
|
|
|
(push (gethash cur-name output) post)
|
|
|
|
|
(remhash cur-name output))
|
|
|
|
|
(t
|
|
|
|
|
(push (gethash cur-name output) pre)
|
|
|
|
|
(remhash cur-name output))))
|
|
|
|
|
(mapconcat 'identity
|
|
|
|
|
(nconc (nreverse pre) (hash-table-values output) (nreverse post))
|
|
|
|
|
" ")))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--render-prompt ()
|
|
|
|
|
"Actually produce the prompt."
|
|
|
|
|
(concat
|
|
|
|
|
(unless (< (line-number-at-pos) 3)
|
|
|
|
|
"\n")
|
|
|
|
|
(eshell-starship--build-module-string)))
|
|
|
|
|
|
|
|
|
|
(defvar-local eshell-starship--last-prompt-info nil
|
|
|
|
|
"A list of the last prompt and the time it took to render it.")
|
2024-01-17 04:16:49 -08:00
|
|
|
|
|
|
|
|
|
(defun eshell-starship--prompt-function ()
|
|
|
|
|
"Function for `eshell-prompt-function'."
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(let (start-time prompt end-time)
|
|
|
|
|
(setq start-time (float-time)
|
|
|
|
|
prompt (eshell-starship--render-prompt)
|
|
|
|
|
end-time (float-time)
|
|
|
|
|
eshell-starship--last-prompt-info
|
|
|
|
|
(list prompt (- end-time start-time)))
|
|
|
|
|
(when (buffer-live-p eshell-starship--current-explain-buffer)
|
|
|
|
|
(with-current-buffer eshell-starship--current-explain-buffer
|
|
|
|
|
(when eshell-starship-explain-auto-update
|
|
|
|
|
(let ((eshell-starship-explain-suppress-refresh-messages t))
|
|
|
|
|
(revert-buffer)))))
|
2024-01-17 04:16:49 -08:00
|
|
|
|
prompt))
|
|
|
|
|
|
2024-12-20 20:22:23 -08:00
|
|
|
|
(defvar-local eshell-starship--restore-state nil
|
2024-01-17 04:16:49 -08:00
|
|
|
|
"State of various variables set by `eshell-starship-prompt-mode'.")
|
|
|
|
|
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(defvar-local eshell-starship--explain-eshell-buffer nil
|
|
|
|
|
"The eshell buffer backing this eshell-starship-explain buffer.")
|
|
|
|
|
|
2024-12-20 20:22:23 -08:00
|
|
|
|
(defun eshell-starship--enable ()
|
|
|
|
|
"Enable eshell-starship."
|
|
|
|
|
(setq-local eshell-starship--restore-state
|
|
|
|
|
(buffer-local-set-state eshell-prompt-function
|
|
|
|
|
'eshell-starship--prompt-function
|
|
|
|
|
eshell-prompt-regexp "^❯ "
|
|
|
|
|
eshell-highlight-prompt nil)
|
2024-12-21 21:38:15 -08:00
|
|
|
|
eshell-starship--module-cache (make-hash-table :test 'equal))
|
|
|
|
|
(add-hook 'eshell-pre-command-hook
|
|
|
|
|
#'eshell-starship--run-module-precmd-actions nil t)
|
|
|
|
|
(add-hook 'eshell-post-command-hook
|
|
|
|
|
#'eshell-starship--run-module-postcmd-actions nil t)
|
2024-12-20 20:22:23 -08:00
|
|
|
|
(add-hook 'eshell-directory-change-hook #'eshell-starship--cwd-clear-caches
|
|
|
|
|
nil t))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--disable ()
|
|
|
|
|
"Disable eshell-starship."
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(when eshell-starship--current-explain-buffer
|
|
|
|
|
(with-current-buffer eshell-starship--current-explain-buffer
|
|
|
|
|
(setq eshell-starship--explain-eshell-buffer nil)))
|
|
|
|
|
(setq-local eshell-starship--module-cache nil
|
|
|
|
|
eshell-starship--current-explain-buffer nil)
|
2024-12-20 20:22:23 -08:00
|
|
|
|
(buffer-local-restore-state eshell-starship--restore-state)
|
2024-12-21 21:38:15 -08:00
|
|
|
|
(remove-hook 'eshell-pre-command-hook
|
|
|
|
|
#'eshell-starship--run-module-precmd-actions t)
|
|
|
|
|
(remove-hook 'eshell-post-command-hook
|
|
|
|
|
#'eshell-starship--run-module-postcmd-actions t)
|
2024-12-20 20:22:23 -08:00
|
|
|
|
(remove-hook 'eshell-directory-change-hook #'eshell-starship--cwd-clear-caches
|
|
|
|
|
t))
|
2024-02-01 12:47:49 -08:00
|
|
|
|
|
2024-01-17 04:16:49 -08:00
|
|
|
|
;;;###autoload
|
|
|
|
|
(define-minor-mode eshell-starship-prompt-mode
|
2024-01-17 21:12:42 -08:00
|
|
|
|
"Minor mode to make eshell prompts look like starship (https://starship.rs)."
|
2024-01-17 04:16:49 -08:00
|
|
|
|
:global nil
|
|
|
|
|
:init-value nil
|
|
|
|
|
:interactive (eshell-mode)
|
|
|
|
|
(if eshell-starship-prompt-mode
|
2024-12-20 20:22:23 -08:00
|
|
|
|
(eshell-starship--enable)
|
|
|
|
|
(eshell-starship--disable)))
|
2024-01-17 04:16:49 -08:00
|
|
|
|
|
2024-12-21 21:38:15 -08:00
|
|
|
|
|
|
|
|
|
;;; Explain buffer
|
|
|
|
|
(defface eshell-starship--heading
|
|
|
|
|
'((t (:height 1.2 :weight bold) ))
|
|
|
|
|
"Face for showing headings in `eshell-starship-explain' buffers.")
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--insert-prompt-string (str &optional prefix first-prefix)
|
|
|
|
|
"Insert STR, a prompt string, into the current buffer.
|
|
|
|
|
This just cleans up STR a bit before inserting it. Also, if PREFIX is non-nil,
|
|
|
|
|
it will be inserted at the start of each line. If FIRST-PREFIX is non-nil, it
|
|
|
|
|
will be used specially as the first line's prefix."
|
|
|
|
|
(cl-loop with first-line = t
|
|
|
|
|
with blank-count = 0
|
|
|
|
|
with found-start = nil
|
|
|
|
|
for line in (string-lines str)
|
|
|
|
|
for empty = (zerop (length line))
|
|
|
|
|
while line
|
|
|
|
|
do
|
|
|
|
|
(if empty
|
|
|
|
|
(when found-start
|
|
|
|
|
(cl-incf blank-count))
|
|
|
|
|
(setq found-start t)
|
|
|
|
|
(dotimes (_ blank-count)
|
|
|
|
|
(insert "\n"))
|
|
|
|
|
(if (and first-line first-prefix)
|
|
|
|
|
(progn
|
|
|
|
|
(insert first-prefix)
|
|
|
|
|
(setq first-line nil))
|
|
|
|
|
(insert prefix))
|
|
|
|
|
(insert line)
|
|
|
|
|
(insert "\n"))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--explain-insert-module (module &optional no-output)
|
|
|
|
|
"Insert information about MODULE at point.
|
|
|
|
|
If NO-OUTPUT is non-nil, don't insert the modules previous output."
|
|
|
|
|
(with-slots (name doc) module
|
|
|
|
|
(let ((bullet-char (if (char-displayable-p ?\•)
|
|
|
|
|
?\•
|
|
|
|
|
?\-))
|
|
|
|
|
(cache-entry (gethash
|
|
|
|
|
name (buffer-local-value
|
|
|
|
|
'eshell-starship--module-cache
|
|
|
|
|
eshell-starship--explain-eshell-buffer))))
|
|
|
|
|
(insert (format " %c %s - %s\n"
|
|
|
|
|
bullet-char
|
|
|
|
|
(propertize name
|
|
|
|
|
'face 'font-lock-keyword-face)
|
|
|
|
|
doc))
|
|
|
|
|
(unless no-output
|
|
|
|
|
(unless (eshell-starship--display-module-p
|
|
|
|
|
module
|
|
|
|
|
(buffer-local-value 'default-directory
|
|
|
|
|
eshell-starship--explain-eshell-buffer))
|
|
|
|
|
(insert " (This module is hidden.)\n"))
|
|
|
|
|
(if (not (cl-first cache-entry))
|
|
|
|
|
(insert " This module has no cached output.\n")
|
|
|
|
|
(insert " Last output was:\n")
|
|
|
|
|
(eshell-starship--insert-prompt-string (cl-second cache-entry)
|
|
|
|
|
" "
|
|
|
|
|
" \"")
|
|
|
|
|
(forward-line -1)
|
|
|
|
|
(end-of-line)
|
|
|
|
|
(insert "\"")
|
|
|
|
|
(forward-line)
|
|
|
|
|
(insert (format " It took %s.\n"
|
|
|
|
|
(eshell-starship-format-span
|
|
|
|
|
(car (cl-third cache-entry)) 3))))
|
|
|
|
|
(insert "\n")))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--explain-insert-enabled ()
|
|
|
|
|
"Insert an explanation of enabled modules at point."
|
|
|
|
|
(let ((rest-modules (copy-hash-table eshell-starship-modules))
|
|
|
|
|
rest-point)
|
|
|
|
|
(dolist (cur-name eshell-starship-module-order)
|
|
|
|
|
(unless (cl-member cur-name eshell-starship-disabled-modules
|
|
|
|
|
:test 'equal)
|
|
|
|
|
(if (eq cur-name t)
|
|
|
|
|
(setq rest-point (point))
|
|
|
|
|
(when-let ((module (gethash cur-name eshell-starship-modules)))
|
|
|
|
|
(eshell-starship--explain-insert-module module)
|
|
|
|
|
(remhash cur-name rest-modules)))))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char rest-point)
|
|
|
|
|
(cl-loop for module being the hash-values of rest-modules
|
|
|
|
|
using (hash-keys name)
|
|
|
|
|
unless (cl-member name eshell-starship-disabled-modules
|
|
|
|
|
:test 'equal)
|
|
|
|
|
do (eshell-starship--explain-insert-module module)))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--explain-format-buffer ()
|
|
|
|
|
"Fill the current buffer with content for `eshell-starship-explain'."
|
|
|
|
|
(unless (buffer-live-p eshell-starship--explain-eshell-buffer)
|
|
|
|
|
(error "Parent Eshell buffer is gone (or no longer using eshell-starship)"))
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
(cl-flet ((heading (txt)
|
|
|
|
|
(propertize txt 'face 'eshell-starship--heading)))
|
|
|
|
|
(cl-destructuring-bind (&optional last-prompt last-time)
|
|
|
|
|
(buffer-local-value 'eshell-starship--last-prompt-info
|
|
|
|
|
eshell-starship--explain-eshell-buffer)
|
|
|
|
|
(when (and last-prompt last-time)
|
|
|
|
|
(insert "The last prompt was:\n")
|
|
|
|
|
(eshell-starship--insert-prompt-string last-prompt " ")
|
|
|
|
|
(insert
|
|
|
|
|
(format "\nIt was rendered in %s.\n\n"
|
|
|
|
|
(eshell-starship-format-span last-time 3)))))
|
|
|
|
|
(insert (heading "The following modules are enabled:\n"))
|
|
|
|
|
(eshell-starship--explain-insert-enabled)
|
|
|
|
|
(if (null eshell-starship-disabled-modules)
|
|
|
|
|
(insert (heading "There are no disabled modules."))
|
|
|
|
|
(insert (heading "The following modules are disabled:\n"))
|
|
|
|
|
(dolist (name eshell-starship-disabled-modules)
|
|
|
|
|
(when-let ((module (gethash name eshell-starship-modules)))
|
|
|
|
|
(eshell-starship--explain-insert-module module t)))
|
|
|
|
|
;; get rid of newline
|
|
|
|
|
(delete-char -1))))
|
|
|
|
|
|
|
|
|
|
(defun eshell-starship--explain-revert (_ignore-auto _noconfirm)
|
|
|
|
|
"Revert function for eshell-starship explain buffers.
|
|
|
|
|
_IGNORE-AUTO and _NOCONFIRM are ignored."
|
|
|
|
|
(let ((save (point))
|
|
|
|
|
(inhibit-read-only t))
|
|
|
|
|
(eshell-starship--explain-format-buffer)
|
|
|
|
|
(goto-char save))
|
|
|
|
|
(unless eshell-starship-explain-suppress-refresh-messages
|
|
|
|
|
(message "Refreshed eshell-starship explain buffer")))
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun eshell-starship-explain-toggle-auto-update-mode (&optional arg)
|
|
|
|
|
"Toggle `eshell-starship-explain-auto-update' in the current buffer.
|
|
|
|
|
If ARG is negative, disable it. If ARG is positive, enable it. Otherwise,
|
|
|
|
|
toggle it."
|
|
|
|
|
(interactive "P" eshell-starship-explain-mode)
|
|
|
|
|
(unless (derived-mode-p 'eshell-starship-explain-mode)
|
|
|
|
|
(error "Not an eshell-starship explain buffer"))
|
|
|
|
|
(if (not arg)
|
|
|
|
|
(cl-callf not eshell-starship-explain-auto-update)
|
|
|
|
|
(let ((num (prefix-numeric-value arg)))
|
|
|
|
|
(setq eshell-starship-explain-auto-update (<= 0 num))))
|
|
|
|
|
(when eshell-starship-explain-auto-update
|
|
|
|
|
(revert-buffer))
|
|
|
|
|
(force-mode-line-update)
|
|
|
|
|
(message "%s auto-updating."
|
|
|
|
|
(if eshell-starship-explain-auto-update
|
|
|
|
|
"Enabled"
|
|
|
|
|
"Disabled")))
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defvar-keymap eshell-starship-explain-mode-map
|
|
|
|
|
:doc "Keymap for `eshell-starship-explain-mode'."
|
|
|
|
|
:parent special-mode-map
|
|
|
|
|
:suppress t
|
|
|
|
|
"a" #'eshell-starship-explain-toggle-auto-update-mode
|
|
|
|
|
"r" #'revert-buffer)
|
|
|
|
|
|
|
|
|
|
(define-derived-mode eshell-starship-explain-mode nil
|
|
|
|
|
"Eshell-Starship Explain"
|
|
|
|
|
"Major mode for `eshell-starship-explain' buffers."
|
|
|
|
|
:group 'eshell-starship
|
|
|
|
|
:interactive nil
|
|
|
|
|
(setq-local mode-name
|
|
|
|
|
'("Eshell-Starship Explain"
|
|
|
|
|
(eshell-starship-explain-auto-update "/a"))
|
|
|
|
|
display-line-numbers nil
|
|
|
|
|
revert-buffer-function
|
|
|
|
|
'eshell-starship--explain-revert))
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun eshell-starship-setup-evil-keybindings ()
|
|
|
|
|
"Setup keybindings for `evil-mode' for eshell-starship."
|
|
|
|
|
(require 'evil)
|
|
|
|
|
(when (fboundp 'evil-define-key*)
|
|
|
|
|
(evil-define-key* '(motion normal) eshell-starship-explain-mode-map
|
|
|
|
|
"a" #'eshell-starship-explain-toggle-auto-update-mode
|
|
|
|
|
"r" #'revert-buffer)))
|
|
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
|
(defun eshell-starship-explain ()
|
|
|
|
|
"Show some information about the current prompt."
|
|
|
|
|
(interactive nil eshell-mode)
|
|
|
|
|
(unless (derived-mode-p 'eshell-mode)
|
|
|
|
|
(error "Current buffer is not in eshell-mode. Nothing to explain"))
|
|
|
|
|
(let ((eshell-buffer (current-buffer))
|
|
|
|
|
(explain-buffer (get-buffer-create "*Eshell-Starship Explain*")))
|
|
|
|
|
(dolist (buffer (buffer-list))
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(cond
|
|
|
|
|
((eq buffer eshell-buffer)
|
|
|
|
|
(setq eshell-starship--current-explain-buffer explain-buffer))
|
|
|
|
|
((derived-mode-p 'eshell-mode)
|
|
|
|
|
(setq eshell-starship--current-explain-buffer nil)))))
|
|
|
|
|
(with-current-buffer explain-buffer
|
|
|
|
|
(unless (derived-mode-p 'eshell-starship-explain-mode)
|
|
|
|
|
(eshell-starship-explain-mode))
|
|
|
|
|
(setq eshell-starship--explain-eshell-buffer eshell-buffer)
|
|
|
|
|
(save-excursion
|
|
|
|
|
(let ((inhibit-read-only t))
|
|
|
|
|
(eshell-starship--explain-format-buffer)))
|
|
|
|
|
(pop-to-buffer (current-buffer)))))
|
|
|
|
|
|
2024-01-17 04:16:49 -08:00
|
|
|
|
(provide 'eshell-starship)
|
|
|
|
|
;;; eshell-starship.el ends here
|