;; PROMPT_COMMAND='export PS1="$(pest)"' (defun string-assoc (key alist) "Shortcut function over :test #'equal for working with TOML alists" (cdr (assoc key alist :test #'equal))) (defun find-config () (let ((config-paths (list (concatenate 'string (uiop:getenv "HOME") "/.config/pest/config.toml") "/etc/pest/config.toml" (concatenate 'string (uiop:getenv "PWD") "./config")))) (loop for path-str in config-paths do (if (probe-file path-str) (return-from find-config path-str))))) (defun config-parse (&optional path) (let ((config-path (if path path (find-config)))) (if config-path (with-open-file (fh config-path :direction :input) (let ((file-content (with-output-to-string (out) (loop for line = (read-line fh nil) while line do (format out "~a~%" line))))) (clop:parse file-content))) (clop:parse " [git] display_head = false display_branch = false git_prefix = \"\" [git.colors] fg = [0, 120, 50] bg = [0, 0, 0] [prompt] display_user = false user_suffix = \"\" display_hostname = false hostname_suffix = \"\" display_pwd = true pwd_suffix = \"\" prompt_char = \" λ \" [prompt.colors] fg = [255, 255, 255] bg = [0, 0, 0] ")))) (defvar *config* NIL) (defun parse-colors (alist) "Given an alist containing the fg and bg lists, extract and flatten the rgb color ints into chlorophyll rgb-colors Returns a list with two elements, the fg chlorophyll rgb object and the bg object" ;; TODO let assignment can be made more compact via lambda for extracting from toml alist eg. with arg "fg" or "bg" (let ((colors (list (string-assoc "fg" (string-assoc "colors" alist)) (string-assoc "bg" (string-assoc "colors" alist))))) (loop for rgb-list in colors collect (destructuring-bind (r g b) rgb-list (chlorophyll:create-rgb-color r g b))))) ;; Battery (defvar *display-battery* NIL) ;; Git (defvar *display-git* NIL) (defvar *git-string* NIL) (defvar *git-style* NIL) (defun make-style (config key) "Given a valid TOML config and key, make the chlorophyll style object for the git status string" (let ((rgb-colors (parse-colors (string-assoc key config)))) (chlorophyll:new-style :bold T :foreground (first rgb-colors) :background (second rgb-colors)))) (defun check-git-enabled (config) (if (or (string-assoc "display_head" (string-assoc "git" config)) (string-assoc "display_branch" (string-assoc "git" config))) T)) (defun check-git-dir () (if (probe-file (pathname (concatenate 'string (get-pwd) "/.git"))) T NIL)) (defun make-git-string (config) "Emit string that contains git information to be printed. Assumes if called that git info is enabled." (if (probe-file (pathname (concatenate 'string (get-pwd) "/.git"))) (let ((git-string NIL)) (if (string-assoc "display_head" (string-assoc "git" config)) (setf git-string (concatenate 'string git-string (legit:current-commit "." :short T)))) (if (string-assoc "display_branch" (string-assoc "git" config)) (progn (if (string-assoc "display_head" (string-assoc "git" config)) (setf git-string (concatenate 'string git-string "|" (legit:current-branch "." :short T))) (setf git-string (concatenate 'string git-string (legit:current-branch "." :short T)))))) ;; This is messy (setf git-string (concatenate 'string (string-assoc "git_prefix" (string-assoc "git" config)) git-string)) git-string))) ;; Prompt (defvar *prompt-style* NIL) (defun get-user () (uiop:getenv "USER")) (defun get-hostname () (machine-instance)) ;; Regex Scanners ;; TODO $HOME rendered as /home/user as opposed to ~ (defvar *home-scan* (ppcre:create-scanner (concatenate 'string "^" (format NIL "~a" (user-homedir-pathname))))) (defun get-pwd () (ppcre:regex-replace *home-scan* (uiop:getenv "PWD") "~/")) (defun make-prompt-string (config) "Given config options, produce prompt string (eg: user@hostname:dir terminator)" (let ((prompt-alist (string-assoc "prompt" config)) (prompt-string NIL)) (if (string-assoc "display_user" prompt-alist) (setf prompt-string (concatenate 'string prompt-string (get-user) (string-assoc "user_suffix" prompt-alist)))) (if (string-assoc "display_hostname" prompt-alist) (setf prompt-string (concatenate 'string prompt-string (get-hostname) (string-assoc "hostname_suffix" prompt-alist)))) (if (string-assoc "display_pwd" prompt-alist) (setf prompt-string (concatenate 'string prompt-string (get-pwd) (string-assoc "pwd_suffix" prompt-alist)))) prompt-string)) (defun reload-config () (setf *config* (config-parse)) (setf *prompt-style* (make-style *config* "prompt")) (if (check-git-enabled *config*) (setf *git-style* (make-style *config* "git")))) (defun render-prompt () "After resolving all config parsing and string generation, render the prompt output here. Produces a stylized string" (format T "~A" (chlorophyll:stylize *prompt-style* (make-prompt-string *config*))) (if (and (check-git-enabled *config*) (check-git-dir)) (format T " ~A" (chlorophyll:stylize *git-style* (make-git-string *config*)))) (format T "~A" (string-assoc "prompt_char" (string-assoc "prompt" *config*)))) (defun main () (reload-config) (render-prompt))