Hi Michael,

Michael Albinus <michael.albi...@gmx.de> 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.

>From 2b6b127a3b6abcad159c4f2cfd07d17b1e2fc468 Mon Sep 17 00:00:00 2001
From: Gene Goykhman <g...@indigo1.com>
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)

Reply via email to