Better distrobox and sudo tramp support

This commit is contained in:
2025-02-17 21:21:52 -08:00
parent 6ea87de1b5
commit b1d77b0f5d
3 changed files with 282 additions and 207 deletions

View File

@ -1,133 +0,0 @@
;;; arch-ros2.el --- Activate and deactivate ROS2 dev environment on ArchLinux -*- lexical-binding: t -*-
;;; Commentary:
;;; Code:
(require 'cl-lib)
(defcustom arch-ros2-root "/opt/ros/humble/"
"Root directory of the ROS2 install."
:type 'directory
:group 'arch-ros2)
(defcustom arch-ros2-distro "humble"
"Version name of ROS2."
:type 'string
:group 'arch-ros2)
(defcustom arch-ros2-version 2
"Version number of ROS2 (probably 2)."
:type 'integer
:group 'arch-ros2)
(defcustom arch-ros2-python-version "3.13"
"Python version of ROS2."
:type 'string
:group 'arch-ros2)
(defvar arch-ros2-active nil
"Weather of not the ROS2 development environment is active.")
(defconst arch-ros2-mode-line-format `(arch-ros2-active
,(propertize "[ROS2]"
'face 'mode-line-emphasis))
"Mode line element for ROS2.")
(defvar arch-ros2--saved-env-vars (make-hash-table :test 'equal)
"Hash table of saved environment variables.
The key of each entry is the variable name. The value is a cons. The car is
either the symbol \\='value or \\='files. If it is \\='value, the cons is a
list of the old value and the value we installed. If the cdr is \\='files, the
value is a list of files to be removed from the variable.")
(defun arch-ros2--set-env-var (var value)
"Set the environment variable VAR to VALUE, saving its old value."
(puthash var (list 'value (getenv var) value) arch-ros2--saved-env-vars)
(setenv var value))
(defun arch-ros2--add-file-to-var (var &rest values)
"Add each of VALUES to the file list environment variable VAR.
This will prepend the values to VAR."
(let* ((cur-val (split-string (or (getenv var) "") ":" t))
(to-set))
(dolist (value values)
(unless (cl-find value cur-val :test 'equal)
(push value to-set)))
(let ((cache (gethash var arch-ros2--saved-env-vars)))
(puthash var (cons 'files (seq-uniq (append to-set (cdr cache))))
arch-ros2--saved-env-vars))
(setenv var (string-join (append to-set cur-val) ":"))))
(defun arch-ros2--add-to-path (&rest values)
"Add each of VALUES to the variable `exec-path'."
(let ((to-check (butlast exec-path))
(did-add nil))
(dolist (value values)
(unless (cl-find value to-check :test 'equal)
(push value exec-path)
(push value did-add)))
(puthash 'exec-path (append did-add
(gethash 'exec-path arch-ros2--saved-env-vars))
arch-ros2--saved-env-vars)))
(defun arch-ros2--restore-env-var (var)
"Restore the value of VAR set with `arch-ros2--set-env-var'."
(let ((entry (gethash var arch-ros2--saved-env-vars)))
(cl-case (car entry)
(value
(cl-destructuring-bind (&optional old-val our-val) (cdr entry)
;; don't restore values that have been changed
(when (equal our-val (getenv var))
(setenv var old-val))))
(files
(when-let ((cur-val (getenv var))
(parts (split-string cur-val ":" t)))
(setenv var (string-join (seq-difference parts (cdr entry)) ":")))))
(remhash var arch-ros2--saved-env-vars)))
(defun arch-ros2-activate ()
"Activate a ROS2 development environment."
(interactive)
(setq arch-ros2-active t)
(add-to-list 'mode-line-misc-info arch-ros2-mode-line-format)
(arch-ros2--add-to-path "/opt/ros/humble/bin/")
(arch-ros2--set-env-var "AMENT_PREFIX_PATH" arch-ros2-root)
(arch-ros2--set-env-var "CMAKE_PREFIX_PATH" arch-ros2-root)
(arch-ros2--set-env-var "COLCON_PREFIX_PATH" arch-ros2-root)
(arch-ros2--set-env-var "ROS_DISTRO" arch-ros2-distro)
(arch-ros2--set-env-var "ROS_LOCALHOST_ONLY" "0")
(arch-ros2--set-env-var "ROS_PYTHON_VERSION"
(car (split-string arch-ros2-python-version "\\.")))
(arch-ros2--set-env-var "ROS_VERSION"
(number-to-string arch-ros2-version))
(arch-ros2--add-file-to-var
"LD_LIBRARY_PATH"
(expand-file-name "opt/rviz_ogre_vendor/lib"
arch-ros2-root)
(expand-file-name "lib"
arch-ros2-root))
(arch-ros2--add-file-to-var
"PKG_CONFIG_PATH" (expand-file-name "lib/pkgconfig" arch-ros2-root))
(let ((python-dir (expand-file-name
(concat "lib/python" arch-ros2-python-version)
arch-ros2-root)))
(arch-ros2--add-file-to-var "PYTHONPATH"
(expand-file-name "dist-packages" python-dir)
(expand-file-name "site-packages" python-dir))))
(defun arch-ros2-deactivate ()
"Deactivate the ROS2 development environment."
(interactive)
(setq arch-ros2-active nil
mode-line-misc-info (cl-remove arch-ros2-mode-line-format
mode-line-misc-info
:test 'equal))
(maphash (lambda (k v)
(cond
((stringp k)
(arch-ros2--restore-env-var k))
((eq k 'exec-path)
(setq exec-path (seq-difference exec-path v))
(remhash 'exec-path arch-ros2--saved-env-vars))))
arch-ros2--saved-env-vars))
(provide 'arch-ros2)
;;; arch-ros2.el ends here

View File

@ -5,6 +5,8 @@
(require 'vc-git)
(require 'eshell)
(require 'cl-lib)
(require 'tramp)
(eval-when-compile (require 'rx))
;;; Configuration options
(defgroup eshell-starship nil
@ -35,7 +37,7 @@ This will also update all eshell-starship explain buffers that need updating."
(revert-buffer)))))))
(defcustom eshell-starship-module-order
'("remote" "cwd" "git" "vc" t "cmd-time" "arrow")
'("remote" "root" "cwd" "git" "vc" t "cmd-time" "newline" "container" "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\"."
@ -58,6 +60,22 @@ appear at most once to denote \"all remaining modules\"."
:tag "Suppress eshell-starship explore refresh messages"
:type 'boolean)
(defcustom eshell-starship-overridden-remote-methods
'("docker" "podman" "kubernetes" "doas" "su" "sudo" "sudoedit")
"List of `file-remote-p' mwthods that should NOT be considered remote.
Any eshell buffer with a `default-directory' managed by one of these methods
will not be considered remote and all modules that would be disabled because of
the remote directory will work as usual."
:group 'eshell-starship
:tag "Overridden Remote Methods"
:type '(repeat (string :tag "Method")))
(defcustom eshell-starship-verbose-tramp 1
"Tramp verbosity level when rendering the prompt."
:group 'eshell-starship
:tag "Tramp Verbosity Level"
:type 'integer)
(defface eshell-starship-icon-face '((t :inherit default))
"Face to use when drawing module icons.
Note that the foreground color will be overridden by the module."
@ -162,7 +180,7 @@ be nil.")
:documentation "Weather the module should be run if
`default-directory' is a `file-remote-p'.")
(action :initarg :action
:initform 'ignore
:initform 'string
:accessor eshell-starship-module-action
:type function
:documentation "A function that produces the main text for the
@ -304,12 +322,16 @@ Example:
(defun eshell-starship--get-current-dir ()
"Get dir for `eshell-starship--prompt-function'."
(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"))
(propertize
(eshell-starship--limit-path-parts
3 (let ((cwd (or (file-remote-p default-directory 'localname)
default-directory)))
(if-let ((worktree (vc-root-dir))
(parent (file-name-parent-directory worktree)))
(file-relative-name cwd (or (file-remote-p parent 'localname)
parent))
(eshell-starship--replace-home-with-tilda cwd))))
'face '(:foreground "dark turquoise"))
(unless (file-writable-p default-directory)
"")))
@ -721,24 +743,56 @@ This does not mean anything if pyenv-mode is not installed.")
;;; Misc modules
(eshell-starship-defmodule remote
:icon "🌐"
:icon "🌐 "
:color "light blue"
:predicate (lambda ()
(file-remote-p default-directory))
:predicate
(lambda ()
(eshell-starship--remote-for-modules-p default-directory))
:action
(lambda ()
(or (file-remote-p default-directory 'host) ""))
:reload-on 'cwd
:doc "A small icon if the working directory is remote.")
(eshell-starship-defmodule root
:predicate
(lambda ()
(member (file-remote-p default-directory 'method)
'("doas" "sudo" "su" "sudoedit")))
:action
(lambda ()
(format "%s in"
(propertize (file-remote-p default-directory 'user)
'face '(:weight bold :foreground "red"))))
:reload-on 'cwd
:doc "Show the current sudo or doas user.")
(eshell-starship-defmodule newline
:predicate 'always
:action (lambda () (propertize "\n" 'read-only t 'rear-nonsticky t))
:doc "A newline in the prompt.")
(eshell-starship-defmodule container
:icon ""
:color "firebrick"
:predicate (lambda ()
(member (file-remote-p default-directory 'method)
'("docker" "podman" "kubernetes")))
:action (lambda ()
(format "[%s]" (file-remote-p default-directory 'host)))
:reload-on 'cwd
:doc "The name of the current container.")
(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)))
(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.")
@ -773,36 +827,52 @@ That is, if EXT is \"pkg.tar.gz\", this will return
(substring name (1+ idx))
""))
(defun eshell-starship--remote-for-modules-p (file)
"Return non-nil if FILE is remote for the purpose of running modules."
(let ((method (file-remote-p file 'method)))
(and method
(not (member method eshell-starship-overridden-remote-methods)))))
(defun eshell-starship--modules-for-dir (dir)
"Return a list of modules that are applicable to DIR."
(seq-uniq
(nconc
(mapcan
(lambda (entry)
(let ((name (car entry))
(is-dir (eq t (file-attribute-type (cdr entry)))))
(if is-dir
(copy-sequence (eshell-starship--module-by :dirs name))
(apply 'nconc
(eshell-starship--module-by :files name)
(mapcar (lambda (ext)
(copy-sequence (eshell-starship--module-by
:extensions ext)))
(eshell-starship--permute-extension
(eshell-starship--file-name-extension name)))))))
(directory-files-and-attributes dir nil nil t))
(let ((default-directory dir))
(cl-loop for (name is-dir module) in eshell-starship--extra-module-files
when (and is-dir (file-directory-p name))
collect module
when (and (not is-dir) (file-exists-p name))
collect module))
(let ((default-directory dir))
(cl-loop for module being the hash-values of eshell-starship-modules
for predicate = (eshell-starship-module-predicate module)
when (funcall predicate)
collect module)))
'eq))
(let ((is-remote (eshell-starship--remote-for-modules-p dir)))
(seq-uniq
(nconc
(cl-delete-if
(lambda (module)
(and is-remote (not (eshell-starship-module-allow-remote-p module))))
(mapcan
(lambda (entry)
(let ((name (car entry))
(is-dir (eq t (file-attribute-type (cdr entry)))))
(if is-dir
(copy-sequence (eshell-starship--module-by :dirs name))
(apply 'nconc
(eshell-starship--module-by :files name)
(mapcar (lambda (ext)
(copy-sequence (eshell-starship--module-by
:extensions ext)))
(eshell-starship--permute-extension
(eshell-starship--file-name-extension name)))))))
(directory-files-and-attributes dir nil nil t)))
(let ((default-directory dir))
(cl-loop for (name is-dir module) in eshell-starship--extra-module-files
when (and (or (not is-remote)
(eshell-starship-module-allow-remote-p module))
is-dir (file-directory-p name))
collect module
when (and (or (not is-remote)
(eshell-starship-module-allow-remote-p module))
(not is-dir) (file-exists-p name))
collect module))
(let ((default-directory dir))
(cl-loop for module being the hash-values of eshell-starship-modules
for predicate = (eshell-starship-module-predicate module)
when (and (or (not is-remote)
(eshell-starship-module-allow-remote-p module))
(funcall predicate))
collect module)))
'eq)))
(defun eshell-starship--propertize-face (str append &rest faces)
"Copy STR and add FACES to its text properties.
@ -902,9 +972,17 @@ Return a hash table mapping module names to their output."
(t
(push (gethash cur-name output) pre)
(remhash cur-name output))))
(mapconcat 'identity
(nconc (nreverse pre) (hash-table-values output) (nreverse post))
" ")))
(cl-loop for (part . rest) = (nconc (nreverse pre)
(hash-table-values output)
(nreverse post))
then rest
while part
concat part
unless (or (string-suffix-p "\n" part)
(string-empty-p part)
(not (car rest))
(string-prefix-p "\n" (car rest)))
concat " ")))
(defun eshell-starship--render-prompt ()
"Actually produce the prompt."
@ -918,7 +996,8 @@ Return a hash table mapping module names to their output."
(defun eshell-starship--prompt-function ()
"Function for `eshell-prompt-function'."
(let (start-time prompt end-time)
(let ((tramp-verbose eshell-starship-verbose-tramp)
start-time prompt end-time)
(setq start-time (float-time)
prompt (eshell-starship--render-prompt)
end-time (float-time)
@ -940,10 +1019,12 @@ Return a hash table mapping module names to their output."
(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)
(buffer-local-set-state
eshell-prompt-function
'eshell-starship--prompt-function
;; temporary fix until the next version where eshell uses fields
eshell-prompt-regexp (rx bol (? "⬢ [" (+ any) "] ") " ")
eshell-highlight-prompt nil)
eshell-starship--module-cache (make-hash-table :test 'equal))
(add-hook 'eshell-pre-command-hook
#'eshell-starship--run-module-precmd-actions nil t)