Re: [PATCH] Provide completion candidates for remote containers over a TRAMP connection

Gene Goykhman <[email protected]>
Newsgroups gmane.emacs.tramp
Message-ID <[email protected]>
Hi Michael,

Michael Albinus <[email protected]> wrote:

> As I have said above: Bind it in a let-clause.

Ok thanks I understand now and have incorporated your suggested change.

> The solution is, that computing tramp-last-hop-directory must happen in
> time, before tramp-default-* variables are let-bound to nil. The
> following has worked in my tests:

Excellent, yes, your solution works well for me.

I have also incorporated your other suggestions and added a `defcustom tramp-completion-remote-containers' to allow the user to turn this feature on. It is off by default, but I can change this if you feel it appropriate.

I have attached the patch in its current form. Note that this patch is now made against the tramp.git repository... my original patch was against the main Emacs repository.
0001-Provide-completion-candidates-for-remote-containers-.patch (text/x-patch, 8.6 KB)
From 2b6b127a3b6abcad159c4f2cfd07d17b1e2fc468 Mon Sep 17 00:00:00 2001
From: Gene Goykhman <[email protected]>
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)
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.