>From 2b6b127a3b6abcad159c4f2cfd07d17b1e2fc468 Mon Sep 17 00:00:00 2001 From: Gene Goykhman Date: Sun, 27 Aug 2023 14:51:03 -0400 Subject: [PATCH] Provide completion candidates for remote containers over a multi-hop connection * lisp/net/tramp.el (tramp--last-hop-directory): Add new variable. (tramp-completion-handle-file-name-all-completions): Use container host directory to execute container program on remote host. (tramp-set-completion-function): FIXME: for now, don't constrain allowable completion methods to only those present on the local system. (tramp-completion-remote-containers): Add customize option to provide completion candidates for containers running on remote hosts. * lisp/tramp-container.el (tramp-container--completion-function): Set default directory to last hop. (tramp-set-completion-function): Don't use executable-find for container program since it might not be running locally. --- lisp/tramp-container.el | 11 +++- lisp/tramp.el | 136 ++++++++++++++++++++++------------------ 2 files changed, 82 insertions(+), 65 deletions(-) diff --git a/lisp/tramp-container.el b/lisp/tramp-container.el index 7f8d4473..e5ad3a55 100644 --- a/lisp/tramp-container.el +++ b/lisp/tramp-container.el @@ -165,7 +165,12 @@ PROGRAM is the program to be run for \"ps\", either This function is used by `tramp-set-completion-function', please see its function help for a description of the format." - (when-let ((default-directory tramp-compat-temporary-file-directory) + ;; Set the default-directory to the directory of the last hop + ;; of a multi-hop path so that we can run the container program + ;; from there. If this is not a multi-hop path, run from the local + ;; temp file directory. + (when-let ((default-directory (or (and tramp-completion-remote-containers tramp--last-hop-directory) + tramp-compat-temporary-file-directory)) (raw-list (shell-command-to-string (concat program " ps --format '{{.ID}}\t{{.Names}}'"))) (lines (split-string raw-list "\n" 'omit)) @@ -383,12 +388,12 @@ see its function help for a description of the format." (tramp-set-completion-function tramp-docker-method `((tramp-container--completion-function - ,(executable-find tramp-docker-program)))) + ,tramp-docker-program))) (tramp-set-completion-function tramp-podman-method `((tramp-container--completion-function - ,(executable-find tramp-podman-program)))) + ,tramp-podman-program))) (tramp-set-completion-function tramp-kubernetes-method diff --git a/lisp/tramp.el b/lisp/tramp.el index 05d28e84..01a83566 100644 --- a/lisp/tramp.el +++ b/lisp/tramp.el @@ -81,6 +81,8 @@ (defvar tramp-file-name-regexp) (defvar tramp-completion-method-regexp) (defvar tramp-completion-file-name-regexp) +(defvar tramp--last-hop-directory nil + "Tracks the directory from which to run container executable programs.") ;; Reload `tramp-compat' when we reload `tramp-autoloads' of the GNU ;; ELPA package. @@ -2141,8 +2143,10 @@ Example: tramp-dns-sd-service-regexp (nth 1 (car v)))) ;; Method. ((string-equal method (nth 1 (car v)))) - ;; Configuration file or empty string. - (t (file-exists-p (nth 1 (car v)))))) + ;; FIXME: for now do not check local existence of file + ;; to allow allow arbitrary container program executable + ;; name for container completion on remote systems. + (t t))) (setq r (delete (car v) r))) (setq v (cdr v))) @@ -2728,16 +2732,8 @@ not in completion mode." "Like `file-name-all-completions' for partial Tramp files." (let ((fullname (tramp-drop-volume-letter (expand-file-name filename directory))) - ;; When `tramp-syntax' is `simplified', we need a default method. - (tramp-default-method - (and (string-empty-p tramp-postfix-method-format) - tramp-default-method)) - (tramp-default-method-alist - (and (string-empty-p tramp-postfix-method-format) - tramp-default-method-alist)) - tramp-default-user tramp-default-user-alist - tramp-default-host tramp-default-host-alist - hop result result1) + (directory (tramp-drop-volume-letter directory)) + tramp--last-hop-directory hop result result1) ;; Suppress hop from completion. (when (string-match @@ -2747,56 +2743,68 @@ not in completion mode." (regexp tramp-postfix-hop-regexp)))) fullname) (setq hop (match-string 1 fullname) - fullname (replace-match "" nil nil fullname 1))) - - ;; Possible completion structures. - (dolist (elt (tramp-completion-dissect-file-name fullname)) - (let* ((method (tramp-file-name-method elt)) - (user (tramp-file-name-user elt)) - (host (tramp-file-name-host elt)) - (localname (tramp-file-name-localname elt)) - (m (tramp-find-method method user host)) - all-user-hosts) - - (unless localname ;; Nothing to complete. - - (if (or user host) - - ;; Method dependent user / host combinations. - (progn - (mapc - (lambda (x) - (setq all-user-hosts - (append all-user-hosts - (funcall (nth 0 x) (nth 1 x))))) - (tramp-get-completion-function m)) - - (setq result - (append result - (mapcar - (lambda (x) - (tramp-get-completion-user-host - method user host (nth 0 x) (nth 1 x))) - (delq nil all-user-hosts))))) - - ;; Possible methods. - (setq result - (append result (tramp-get-completion-methods m hop))))))) - - ;; Unify list, add hop, remove nil elements. - (dolist (elt result) - (when elt - (setq elt (replace-regexp-in-string - tramp-prefix-regexp (concat tramp-prefix-format hop) elt)) - (push (substring elt (length directory)) result1))) - - ;; Complete local parts. - (delete-dups - (append - result1 - (ignore-errors - (tramp-run-real-handler - #'file-name-all-completions (list filename directory))))))) + fullname (replace-match "" nil nil fullname 1) + tramp--last-hop-directory + (tramp-make-tramp-file-name (tramp-dissect-hop-name hop)))) + + (let (;; When `tramp-syntax' is `simplified', we need a default method. + (tramp-default-method + (and (string-empty-p tramp-postfix-method-format) + tramp-default-method)) + (tramp-default-method-alist + (and (string-empty-p tramp-postfix-method-format) + tramp-default-method-alist)) + tramp-default-user tramp-default-user-alist + tramp-default-host tramp-default-host-alist) + + ;; Possible completion structures. + (dolist (elt (tramp-completion-dissect-file-name fullname)) + (let* ((method (tramp-file-name-method elt)) + (user (tramp-file-name-user elt)) + (host (tramp-file-name-host elt)) + (localname (tramp-file-name-localname elt)) + (m (tramp-find-method method user host)) + all-user-hosts) + + (unless localname ;; Nothing to complete. + + (if (or user host) + + ;; Method dependent user / host combinations. + (progn + (mapc + (lambda (x) + (setq all-user-hosts + (append all-user-hosts + (funcall (nth 0 x) (nth 1 x))))) + (tramp-get-completion-function m)) + + (setq result + (append result + (mapcar + (lambda (x) + (tramp-get-completion-user-host + method user host (nth 0 x) (nth 1 x))) + (delq nil all-user-hosts))))) + + ;; Possible methods. + (setq result + (append result (tramp-get-completion-methods m hop))))))) + + ;; Unify list, add hop, remove nil elements. + (dolist (elt result) + (when elt + (setq elt (replace-regexp-in-string + tramp-prefix-regexp (concat tramp-prefix-format hop) elt)) + (push (substring elt (length directory)) result1))) + + ;; Complete local parts. + (delete-dups + (append + result1 + (ignore-errors + (tramp-run-real-handler + #'file-name-all-completions (list filename directory)))))))) ;; Method, host name and user name completion for a file. (defun tramp-completion-handle-file-name-completion @@ -3002,6 +3010,10 @@ This function is added always in `tramp-get-completion-function' for all methods. Resulting data are derived from default settings." `((,(tramp-find-user method nil nil) ,(tramp-find-host method nil nil)))) +(defcustom tramp-completion-remote-containers nil + "Whether container hosts in multi-hop paths should be queried for completions." + :type 'boolean) + (defcustom tramp-completion-use-auth-sources auth-source-do-cache "Whether to use `auth-source-search' for completion of user and host names. This could be disturbing, if it requires a password / passphrase, -- 2.39.2 (Apple Git-143)