Better distrobox and sudo tramp support

This commit is contained in:
Alexander Rosenberg 2025-02-17 21:21:52 -08:00
parent 6ea87de1b5
commit b1d77b0f5d
Signed by: Zander671
GPG Key ID: 5FD0394ADBD72730
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 'vc-git)
(require 'eshell) (require 'eshell)
(require 'cl-lib) (require 'cl-lib)
(require 'tramp)
(eval-when-compile (require 'rx))
;;; Configuration options ;;; Configuration options
(defgroup eshell-starship nil (defgroup eshell-starship nil
@ -35,7 +37,7 @@ This will also update all eshell-starship explain buffers that need updating."
(revert-buffer))))))) (revert-buffer)))))))
(defcustom eshell-starship-module-order (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. "The order of modules for eshell-starship.
This is a list with each element being a module name. The special value t can 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\"." 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" :tag "Suppress eshell-starship explore refresh messages"
:type 'boolean) :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)) (defface eshell-starship-icon-face '((t :inherit default))
"Face to use when drawing module icons. "Face to use when drawing module icons.
Note that the foreground color will be overridden by the module." 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 :documentation "Weather the module should be run if
`default-directory' is a `file-remote-p'.") `default-directory' is a `file-remote-p'.")
(action :initarg :action (action :initarg :action
:initform 'ignore :initform 'string
:accessor eshell-starship-module-action :accessor eshell-starship-module-action
:type function :type function
:documentation "A function that produces the main text for the :documentation "A function that produces the main text for the
@ -304,11 +322,15 @@ Example:
(defun eshell-starship--get-current-dir () (defun eshell-starship--get-current-dir ()
"Get dir for `eshell-starship--prompt-function'." "Get dir for `eshell-starship--prompt-function'."
(concat (concat
(propertize (eshell-starship--limit-path-parts (propertize
3 (if-let ((worktree (vc-root-dir)) (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))) (parent (file-name-parent-directory worktree)))
(file-relative-name default-directory parent) (file-relative-name cwd (or (file-remote-p parent 'localname)
(eshell-starship--replace-home-with-tilda default-directory))) parent))
(eshell-starship--replace-home-with-tilda cwd))))
'face '(:foreground "dark turquoise")) 'face '(:foreground "dark turquoise"))
(unless (file-writable-p default-directory) (unless (file-writable-p default-directory)
""))) "")))
@ -723,22 +745,54 @@ This does not mean anything if pyenv-mode is not installed.")
(eshell-starship-defmodule remote (eshell-starship-defmodule remote
:icon "🌐 " :icon "🌐 "
:color "light blue" :color "light blue"
:predicate (lambda () :predicate
(file-remote-p default-directory)) (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.") :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 (eshell-starship-defmodule arrow
:predicate 'always :predicate 'always
:reload-on 'always :reload-on 'always
:action (lambda () :action (lambda ()
(concat
(propertize "\n" 'read-only t 'rear-nonsticky t)
(propertize (propertize
" " 'face `(:foreground " " 'face `(:foreground
,(if (= eshell-last-command-status 0) ,(if (= eshell-last-command-status 0)
"lime green" "lime green"
"red")) "red"))
'rear-nonsticky t))) 'rear-nonsticky t))
:doc "An arrow that appears next to where you type.") :doc "An arrow that appears next to where you type.")
@ -773,10 +827,20 @@ That is, if EXT is \"pkg.tar.gz\", this will return
(substring name (1+ idx)) (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) (defun eshell-starship--modules-for-dir (dir)
"Return a list of modules that are applicable to DIR." "Return a list of modules that are applicable to DIR."
(let ((is-remote (eshell-starship--remote-for-modules-p dir)))
(seq-uniq (seq-uniq
(nconc (nconc
(cl-delete-if
(lambda (module)
(and is-remote (not (eshell-starship-module-allow-remote-p module))))
(mapcan (mapcan
(lambda (entry) (lambda (entry)
(let ((name (car entry)) (let ((name (car entry))
@ -790,19 +854,25 @@ That is, if EXT is \"pkg.tar.gz\", this will return
:extensions ext))) :extensions ext)))
(eshell-starship--permute-extension (eshell-starship--permute-extension
(eshell-starship--file-name-extension name))))))) (eshell-starship--file-name-extension name)))))))
(directory-files-and-attributes dir nil nil t)) (directory-files-and-attributes dir nil nil t)))
(let ((default-directory dir)) (let ((default-directory dir))
(cl-loop for (name is-dir module) in eshell-starship--extra-module-files (cl-loop for (name is-dir module) in eshell-starship--extra-module-files
when (and is-dir (file-directory-p name)) when (and (or (not is-remote)
(eshell-starship-module-allow-remote-p module))
is-dir (file-directory-p name))
collect module collect module
when (and (not is-dir) (file-exists-p name)) when (and (or (not is-remote)
(eshell-starship-module-allow-remote-p module))
(not is-dir) (file-exists-p name))
collect module)) collect module))
(let ((default-directory dir)) (let ((default-directory dir))
(cl-loop for module being the hash-values of eshell-starship-modules (cl-loop for module being the hash-values of eshell-starship-modules
for predicate = (eshell-starship-module-predicate module) for predicate = (eshell-starship-module-predicate module)
when (funcall predicate) when (and (or (not is-remote)
(eshell-starship-module-allow-remote-p module))
(funcall predicate))
collect module))) collect module)))
'eq)) 'eq)))
(defun eshell-starship--propertize-face (str append &rest faces) (defun eshell-starship--propertize-face (str append &rest faces)
"Copy STR and add FACES to its text properties. "Copy STR and add FACES to its text properties.
@ -902,9 +972,17 @@ Return a hash table mapping module names to their output."
(t (t
(push (gethash cur-name output) pre) (push (gethash cur-name output) pre)
(remhash cur-name output)))) (remhash cur-name output))))
(mapconcat 'identity (cl-loop for (part . rest) = (nconc (nreverse pre)
(nconc (nreverse pre) (hash-table-values output) (nreverse post)) (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 () (defun eshell-starship--render-prompt ()
"Actually produce the prompt." "Actually produce the prompt."
@ -918,7 +996,8 @@ Return a hash table mapping module names to their output."
(defun eshell-starship--prompt-function () (defun eshell-starship--prompt-function ()
"Function for `eshell-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) (setq start-time (float-time)
prompt (eshell-starship--render-prompt) prompt (eshell-starship--render-prompt)
end-time (float-time) end-time (float-time)
@ -940,9 +1019,11 @@ Return a hash table mapping module names to their output."
(defun eshell-starship--enable () (defun eshell-starship--enable ()
"Enable eshell-starship." "Enable eshell-starship."
(setq-local eshell-starship--restore-state (setq-local eshell-starship--restore-state
(buffer-local-set-state eshell-prompt-function (buffer-local-set-state
eshell-prompt-function
'eshell-starship--prompt-function 'eshell-starship--prompt-function
eshell-prompt-regexp "^ " ;; temporary fix until the next version where eshell uses fields
eshell-prompt-regexp (rx bol (? "⬢ [" (+ any) "] ") " ")
eshell-highlight-prompt nil) eshell-highlight-prompt nil)
eshell-starship--module-cache (make-hash-table :test 'equal)) eshell-starship--module-cache (make-hash-table :test 'equal))
(add-hook 'eshell-pre-command-hook (add-hook 'eshell-pre-command-hook

161
init.el
View File

@ -227,12 +227,32 @@ Interactively, force the recompile if called with a prefix."
(use-package tramp (use-package tramp
:ensure nil :ensure nil
:config :config
(add-to-list 'tramp-connection-properties
(list (rx bos "/" (or "podman" "docker") ":")
"direct-async-process" t))
(add-to-list 'tramp-connection-properties
(list (rx bos "/" (or "ssh" "sshx") ":")
"direct-async-process" t))
(add-to-list 'tramp-connection-properties
(list (rx bos "/" (or "sudo" "su" "doas"
"sudoedit")
":")
"direct-async-process" t))
(connection-local-set-profile-variables (connection-local-set-profile-variables
'remote-direct-async-process 'error-only
'((tramp-direct-async-process . t))) '((tramp-verbose . 1)))
(connection-local-set-profiles (connection-local-set-profiles
'(:protocol "ssh") '(:method "sudo")
'remote-direct-async-process)) 'error-only)
(connection-local-set-profiles
'(:method "doas")
'error-only)
(connection-local-set-profiles
'(:method "su")
'error-only)
(connection-local-set-profiles
'(:method "sudoedit")
'error-only))
(use-package midnight (use-package midnight
:ensure nil :ensure nil
@ -1762,9 +1782,6 @@ otherwise, call `bibtex-find-text'."
(use-package pyvenv) (use-package pyvenv)
(use-package pyenv-mode) (use-package pyenv-mode)
;; My dev environment for ROS2
(require 'arch-ros2)
;; java-ts-mode ;; java-ts-mode
(use-package java-ts-mode (use-package java-ts-mode
:hook ((java-ts-mode . trusted-files-eglot-ensure-if-safe) :hook ((java-ts-mode . trusted-files-eglot-ensure-if-safe)
@ -2464,7 +2481,7 @@ argument."
:hook ((eshell-load . eat-eshell-visual-command-mode) :hook ((eshell-load . eat-eshell-visual-command-mode)
(eshell-mode . eat-eshell-mode) (eshell-mode . eat-eshell-mode)
(eshell-mode . my/-eshell-mode-setup) (eshell-mode . my/-eshell-mode-setup)
(eshell-directory-change . my/-eshell-maybe-setup-remote-aliases)) (eshell-directory-change . my/-eshell-maybe-setup-remote))
:bind (:map eshell-mode-map :bind (:map eshell-mode-map
("TAB" . completion-at-point) ("TAB" . completion-at-point)
("<tab>" . completion-at-point)) ("<tab>" . completion-at-point))
@ -2479,14 +2496,73 @@ argument."
(or " " eos)) (or " " eos))
(cl-second elt)))) (cl-second elt))))
eshell-command-aliases-list)) eshell-command-aliases-list))
(defun my/-eshell-maybe-setup-remote-aliases ()
(if (file-remote-p default-directory) (defun my/-eshell-container-p (&optional dir)
(member (file-remote-p default-directory 'method)
'("docker" "podman")))
(defun my/-eshell-distrobox-p (&optional dir)
(and (my/-eshell-container-p dir)
(let ((default-directory (or dir default-directory)))
(executable-find "distrobox-host-exec" t))))
(defun my/-eshell-sudo-p (&optional dir)
(member (file-remote-p (or dir default-directory) 'method)
'("sudo" "doas" "su" "sudoedit")))
(defun my/-eshell-really-remote-p (&optional dir)
(and (file-remote-p (or dir default-directory))
(not (my/-eshell-distrobox-p dir))
(not (my/-eshell-sudo-p dir))))
(defun eshell/-captive-cd (&optional dir &rest _)
(cond
((not dir)
(eshell/-captive-cd "~"))
((or (not (file-remote-p default-directory))
(file-remote-p dir))
(eshell/cd dir))
((file-name-absolute-p dir)
(eshell/cd (concat (file-remote-p default-directory) dir)))
(t
(eshell/cd dir))))
(defvar-local my/-eshell-last-remote-system nil)
(defun my/-eshell-maybe-setup-remote (&optional force)
(when (or force (not (equal my/-eshell-last-remote-system
(file-remote-p default-directory))))
(kill-local-variable 'eshell-syntax-highlighting-highlight-in-remote-dirs)
(if (my/-eshell-really-remote-p)
(setq-local eshell-command-aliases-list (my/-eshell-filter-alias-list)) (setq-local eshell-command-aliases-list (my/-eshell-filter-alias-list))
(kill-local-variable 'eshell-command-aliases-list))) (setq-local eshell-command-aliases-list
(default-toplevel-value 'eshell-command-aliases-list)))
(setq-local eshell-command-aliases-list
(copy-tree eshell-command-aliases-list))
(when (file-remote-p default-directory)
(add-to-list 'eshell-command-aliases-list '("cd" "-captive-cd $1") t))
(when (or (my/-eshell-distrobox-p)
(my/-eshell-sudo-p))
(setq-local eshell-syntax-highlighting-highlight-in-remote-dirs t)
(setf (alist-get "pwd" eshell-command-aliases-list nil nil 'equal)
'("(directory-file-name (file-remote-p default-directory 'localname))")))
(when (my/-eshell-distrobox-p)
(unless (executable-find "eza" t)
(if (executable-find "exa" t)
(setf (alist-get "ls" eshell-command-aliases-list nil nil 'equal)
'("exa -F $*"))
(setf (alist-get "ls" eshell-command-aliases-list nil t 'equal)
nil)))
(unless (executable-find "trash-put" t)
(setf (alist-get "tp" eshell-command-aliases-list nil t 'equal) nil
(alist-get "trr" eshell-command-aliases-list nil t 'equal) nil
(alist-get "tre" eshell-command-aliases-list nil t 'equal) nil
(alist-get "trm" eshell-command-aliases-list nil t 'equal) nil
(alist-get "rm" eshell-command-aliases-list nil t 'equal) nil))
(setf (alist-get "ldg" eshell-command-aliases-list nil t 'equal) nil)))
(setq-local my/-eshell-last-remote-system
(file-remote-p default-directory)))
(defun my/-eshell-mode-setup () (defun my/-eshell-mode-setup ()
"Setup function run from `eshell-mode-hook'" "Setup function run from `eshell-mode-hook'"
(setq-local corfu-auto nil) (setq-local corfu-auto nil)
(my/-eshell-maybe-setup-remote-aliases)) (my/-eshell-maybe-setup-remote t))
(setq-default eshell-command-aliases-list (setq-default eshell-command-aliases-list
'(("clear" "clear t") '(("clear" "clear t")
("e" "find-file $1") ("e" "find-file $1")
@ -2507,10 +2583,10 @@ argument."
("tp" "trash-put $*") ("tp" "trash-put $*")
("trr" "trash-restore $*") ("trr" "trash-restore $*")
("tre" "trash-empty $*") ("tre" "trash-empty $*")
("tre" "trash-empty $*")
("trm" "trash-rm $*") ("trm" "trash-rm $*")
("rm" "echo 'rm: I''m unsafe! Don''t use me.'; false") ("rm" "echo 'rm: I''m unsafe! Don''t use me.'; false")
("\\rm" "eshell/rm"))) ("\\rm" "eshell/rm")))
(defvar my/eshell-bm-auto-ls t (defvar my/eshell-bm-auto-ls t
"Weather or not to run ls after `eshell/bm'") "Weather or not to run ls after `eshell/bm'")
(defun eshell/bm (&optional name) (defun eshell/bm (&optional name)
@ -2518,10 +2594,61 @@ argument."
If no name is given, list all bookmarks instead." If no name is given, list all bookmarks instead."
(if name (if name
(progn (progn
(eshell/cd (bookmark-get-filename name)) (string-match (rx bos (group (* (not "/"))) (* "/") (group (* any)))
name)
(let* ((bm-name (match-string 1 name))
(after-path (match-string 2 name))
(bm-path (bookmark-get-filename bm-name))
(full-path (expand-file-name after-path bm-path)))
(when (my/-eshell-distrobox-p)
(setq full-path (concat (file-remote-p default-directory)
full-path)))
(if (not (file-directory-p full-path))
(progn
(find-file full-path)
(goto-char (bookmark-get-position bm-name)))
(eshell/cd full-path)
(when my/eshell-bm-auto-ls (when my/eshell-bm-auto-ls
(eshell/ls))) (eshell/ls)))))
(eshell-print (string-join (bookmark-all-names) " "))))) (bookmark-maybe-load-default-file)
(eshell-print
(mapconcat (lambda (record)
(let ((name (bookmark-name-from-full-record record))
(file (bookmark-get-filename record)))
(format "%s => %s"
(propertize name 'face '(:foreground "deep sky blue"
:weight bold))
(if (file-directory-p file)
(file-name-as-directory file)
(directory-file-name file)))))
bookmark-alist
"\n"))))
(defun pcomplete/bm ()
"Completions for `bm'."
(let ((arg (pcomplete-arg)))
(if (not (cl-find ?/ arg))
(pcomplete-here (mapcar (##concat % "/") (bookmark-all-names)))
(when (string-match (rx bos (group (+ (not "/"))) (+ "/") (group (* any)))
arg)
(let ((bm-name (match-string 1 arg))
(after-path (match-string 2 arg)))
(when-let ((base (ignore-errors (bookmark-get-filename bm-name)))
((file-directory-p base))
(abs-path (expand-file-name after-path base))
(dir-path (if (string-empty-p after-path)
abs-path
(file-name-directory abs-path)))
(path-end (if (string-empty-p after-path)
""
(file-name-nondirectory abs-path))))
(pcomplete-here
(mapcan (lambda (entry)
(unless (member (car entry) '(".." "."))
(if (eq t (file-attribute-type (cdr entry)))
(list (concat (car entry) "/"))
(list (car entry)))))
(directory-files-and-attributes dir-path))
path-end))))))))
(use-package esh-help (use-package esh-help
:hook (eshell-mode . my/-setup-eshell-help-func) :hook (eshell-mode . my/-setup-eshell-help-func)
:init :init
@ -2547,7 +2674,7 @@ If `default-directory' is remote, call `my/project-eat-or-default'. Otherwise,
call `my/project-eshell-or-default'. ARG is the same as for either of the above call `my/project-eshell-or-default'. ARG is the same as for either of the above
functions (only eshell uses it at the time of writing)." functions (only eshell uses it at the time of writing)."
(interactive "P") (interactive "P")
(if (file-remote-p default-directory) (if (my/-eshell-really-remote-p)
(my/project-eat-or-default) (my/project-eat-or-default)
(my/project-eshell-or-default arg))) (my/project-eshell-or-default arg)))
(keymap-global-set "C-c v" #'my/open-shell-dwim) (keymap-global-set "C-c v" #'my/open-shell-dwim)