[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connec
From: |
Jordan Ellis Coppard |
Subject: |
Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connections` |
Date: |
Tue, 21 Jan 2025 22:14:10 +0900 |
Hello Michael another long one from me,
I realise backtick/grave ` has meaning in elisp but habit (and I can't
think of a good alternative) makes me use it to delimit code, so unless
specified no quoted forms are intended when I use ` here hehe!
So for the customisation things, I looked at the documentation again and
I got something basic scaffolded in principle but ran into a tangential
issue while doing so, I'll explain for full context (and I have the code
afterwards):
I want that custom filter command as mentioned, then I also thought
about altering how the names of the containers appear in the completion
list and during that process I couldn't figure out how to nicely NOT use
prior connection history and other automatically parsed connection
information. While testing this I noticed after I remove my Tramp
history file manually and restart Emacs that some bogus Tramp cache data
was still present.
(1) `save-place-mode` clobbers Tramps cache
It turns out that `save-place-mode` is corrupting Tramp's cache. My
guess is that when Emacs starts and `save-hist-mode` loads its history
file which is called `places` (by default in the users Emacs
configuration directory) that the Tramp file name handler is triggered
as said `places` file is a literal elisp file with a list of cons pairs
of file name and data (point, dired info etc), for example:
;;; -*- coding: utf-8; mode: lisp-data -*-
(("/podmancp:quizzical_tharp:/home/jammy/project/build.zig" . 1474))
Loading Emacs with the Tramp cache file deleted, and with an Emacs
configuration that enables save-hist-mode with `places` as above and
then typing `C-x C-f /podmancp:` lists a real container `jam-zevem`, and
`quizzical_tharp` the latter of which long-since hasn't existed.
If I do the same but delete my `places` and `tramp` history/cache files
only `jam-zevem` is listed, and when Emacs is closed the resulting Tramp
cache is minimal with the expected values. If Tramp has populated its
cache from `places` the saved Tramp cache `tramp` is polluted with
entries which could make zero sense with no easy/obvious way for the
user to remove them since they'll just be loaded again implicitly via
`save-hist-mode`.
I suspect this might also cause hangs in the wild for users who use
`save-hist-mode` too. It's not enabled by default but as I'm sure you've
guessed is in my config.
Disabling save-hist-mode integration (or changing it so it doesn't
clobber Tramp) by default is the solution here I think; since
save-hist-mode looks like it can (by default) keep history that is very
ancient. Optional integration allowing users to opt-in would prevent
nasty surprises like my case here.
(2) Tramp and forgetting projects
If I attempt to forget a project.el project with
`project-forget-project` Tramp will attempt to establish a connection
which can be frustrating if the project doesn't exist anymore (moved
directories, changed host configuration, any number of reasons) and not
only causes a pause in the best-case (during interactive minibuffer
completion) but in worst case can freeze Emacs until C-g is issued. I
believe a timeout via `remote-file-name-access-timeout` can be
configured but a user would still then have to wait the timeout time (or
know to press C-g) just to forget a project. I might be ignorant of
potential hooks people may(not) use in which case Tramp probably should
connect so those hooks can run (imagine a hook to delete the files
executes when a project is forgotten). Perhaps (if those sorts of things
do exist/occur) Tramp should only attempt to connect after interactive
completion has returned a value, and not simply while browsing with C-n
and C-p; or have it configurable.
I re-define `project-forget-project` in my init.el which is the same as
the default implementation but with `non-essential` lexically bound to
stop Tramp doing this:
(defun project-forget-project (project-root)
"Remove directory PROJECT-ROOT from the project list.
PROJECT-ROOT is the root directory of a known project listed in
the project list."
(interactive (list (funcall project-prompter)))
(let ((non-essential t)) ; JORDAN: My single change, rest is verbatim
from project.el sources.
(project--remove-from-project-list
project-root "Project `%s' removed from known projects")))
I see other areas of project.el use non-essential in consideration of
possible Tramp interaction, perhaps Tramp and/or project.el need
adjustments here too (or maybe my single line patch here is such a
solution for upstream; albeit defaults to always never connecting).
(3) Blessed custom approach clobbers the method it uses
Now I know why certain completions are appearing in my minibuffer which
shouldn't be (save-hist-mode clobbering Tramp cache) I got back to the
customised podmancp connection stuff.
While "(tramp) Customizing Completion" does outline a way it only seems
possible to have the customised completion take effect all the time, or
none of the time (will get back to this).
These development "jam" containers have a well-known user `jammy`, and
to avoid having to type /podmancp:jammy@ all the time I made an
abbreviation as Tramp documents:
(defun tjp/abbrev-no-expand-char () t)
(put 'tjp/abbrev-no-expand-char 'no-self-insert t)
(define-abbrev-table 'my-tramp-abbrev-table
'(("jam" "/podmancp:jammy@" tjp/abbrev-no-expand-char)))
(add-hook
'minibuffer-setup-hook
(lambda ()
(abbrev-mode 1)
(setq local-abbrev-table my-tramp-abbrev-table)))
(advice-add 'minibuffer-complete
:before 'expand-abbrev)
This is fine and it does work -- I note that the Tramp docs may also
want to include the `'no-self-insert` snippet otherwise after expanding
the abbreviation one must backspace before being able to type (or view)
completions.
The problem arises whereby I have no nice way (that I see/understand
currently) to conditionally apply my custom filter. The filters apply to
the podmancp method which includes podmancp:jammy or
podmancp:foo:~/foo/bar or podmancp:lorem@ipsum:/root/foo.txt and so
forth; so long as the method is podmancp either I must always use my
custom filter (bad, now I've clobbered podmancp globally) or I don't
have access to it.
Said custom filter (the single change is in the arguments to
shell-command-to-string) is:
(defun tjp/jam--tramp-completion-function (method)
(tramp-skeleton-completion-function method
(when-let* ((raw-list
(shell-command-to-string
(concat program " ps -a --filter 'label=sh.jammy.box' --format
'{{.ID}}\t{{.Names}}'")))
(lines (split-string raw-list "\n" 'omit))
(names
(tramp-compat-seq-keep
(lambda (line)
(when (string-match
(rx bol (group (1+ nonl))
;; Could remove prefix if Tramp connection login args add it
back e.g. jam-%h. Maybe Emacs completion backend has annotated values
which could do this also but the jam prefix is intended to be a very
"hardcoded" value and not fluid. Jam containers are dev environments.
;; "\t" (? "jam-")
(? (group (1+ nonl))) eol)
"\t" (? (group (1+
nonl))) eol)
line)
(or (match-string 2 line)
(match-string 1 line))))
lines)))
(mapcar (lambda (name) (list nil name)) names))))
(tramp-set-completion-function "podmancp"
'((tjp/jam--tramp-completion-function "podmancp")))
So if there were a way to use this completion function for a specific
method/host/user combination, or alternately regexp similarly to how one
can define different connection properties as "(tramp) Predefined
connection information") explains I think this would be solved.
While that is also a lot of code and requires consulting abbrev-mode the
alternative which works today and doesn't clobber podmancp is creating a
new method entirely (backtick ` is for elisp in this snippet):
(setq jam--devbox
`("jam"
(tramp-login-program ,tramp-podman-method)
(tramp-login-args (("exec")
("-it")
("-u" "jammy")
("--workdir"
"/home/jammy/project")
("%h")
("%l")))
(tramp-direct-async (,tramp-default-remote-shell "-c"))
(tramp-remote-shell ,tramp-default-remote-shell)
(tramp-remote-shell-login ("-l"))
(tramp-remote-shell-args ("-i" "-c"))
(tramp-copy-program ,tramp-podman-method)
(tramp-copy-args (("cp")))
(tramp-copy-file-name (("%h" ":") ("%f")))
(tramp-copy-recursive t)))
(tramp-set-completion-function "jam"
`((tjp/jam--tramp-completion-function "jam")))
(add-to-list 'tramp-methods jam--devbox)
The disadvantage here being that one must either know all of what to
define for new Tramp methods or learn that (by looking at Tramp's source
code as I did) and still most is a mere copy-paste. Also completion
feels better since typing /jam: doesn't require managing abbreviations,
manually expanding them, and adding logic to remove the expansion character.
Finally while trying to conditionally apply things, and while I was
trying to figure out where the clobbered completions (now known from
save-hist-mode) were coming from I was doing things like this:
(defconst bingbong ((tramp-set-completion-function "podmancp"
'((tjp/jam--tramp-completion-function "podmancp")))))
;; (defconst bingbong (list '(tramp-completion-use-auth-sources nil)
'(tramp-completion-use-cache t)))
;; (defconst bingbong (list '(tramp-completion-use-auth-sources t)
'(tramp-completion-use-cache nil)))
;; (setq tramp-completion-use-cache t)
;; (setq tramp-completion-use-auth-sources t)
(connection-local-set-profile-variables
'bingbong-prof
bingbong)
(connection-local-set-profiles
`(:application tramp :protocol "podmancp" :user "jammy")
'bingbong-prof)
If that is the blessed route this becomes quite a lot of code surface
area for something that feels like it should be simple, and in any case
the interplay between when connection local variables are applied and
when Tramp reads them, Tramps own connection properties and when they
are available/read etc is a bit complex to me still.
I had success with the immediate above snippet.. I think.. because I
could never get it consistent and using M-x profiler to help determine
backtraces etc, or short of stepping through with edebug et al; or
reading much more documentation versus just using a complete custom
method and saving a lot of time. I am for reading documentation of
course, but getting into the weeds of connection-local variables and
friends (in addition to abbrev-mode before) is another fork in the path
which builds up. In this specific case it's quite trivial too: just
being able to list containers with a specific label.
That said having to make a custom method every time isn't very
composable, perhaps doing all of this "at Tramp" is the wrong area and
some external successive filtering is required. Maybe some kind of
Orderless (or alternative) completion style so that if I type (for
example) ?jam Ordlerless further filters.. I don't know. The domain of
responsibility here is murky to me.
(4) delete-process
I still think delete-process (original topic in this thread) should be
changed hehe; my fault for adding more topics continually and in this
very reply too!
On 19/1/2025 8:26 pm, Michael Albinus wrote:
Well. For more than 20 years, I'm the Tramp maintainer. Other people do
contribute (I'm very grateful for this!), but in general this is a
one-man-show. I would appreciate much, if somebody (you?) could
contribute on a regular basis.
If I have things to contribute I'd love to; I'm already behind on a
bunch of other work currently and I don't feel like I understand more
than the trivial parts of Tramp but if there's something in future that
I can I will try.
I will say when I first checked the tramp-devel mailing list the low
activity (for whatever reason, and its my internalisation) made me think
about trying harder to solve issues before sending an email only because
I imagine (and you confirm) Tramp is probably a one-man-show and I think
I would've felt bad bothering so eagerly.. but then I tried too hard and
frustrated myself. You can imagine the extreme end of eagerness is to
immediately send a "how do I use this" to the mailing list (instead of
reading the documentation) -- it's tough to balance trying to hack a
solution together yourself versus when it's time to just email asking
for help. I expect your opinion is "just ask if you're stuck!".
PS: Tramp, like Emacs, is copyrighted to the FSF. If somebody wants to
contribute more than a small change (up to 15 lines), we need to sign
legal papers for the FSF copyrights. Are you willing to do so?
If you want to do this ahead of time I am willing, or at the time of
first contribution (if any) it makes no difference to me.
/Jordan
- Refrain from immediate `delete-process` in `tramp-cleanup-all-connections`, Jordan Ellis Coppard, 2025/01/17
- Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connections`, Michael Albinus, 2025/01/17
- Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connections`, Jordan Ellis Coppard, 2025/01/18
- Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connections`, Michael Albinus, 2025/01/18
- Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connections`, Jordan Ellis Coppard, 2025/01/18
- Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connections`, Jordan Ellis Coppard, 2025/01/18
- Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connections`, Michael Albinus, 2025/01/19
- Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connections`, Jordan Ellis Coppard, 2025/01/21
- Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connections`, Michael Albinus, 2025/01/19
- Re: Refrain from immediate `delete-process` in `tramp-cleanup-all-connections`,
Jordan Ellis Coppard <=