From 15380908891516f20f51ade422b25e5dff2115aa Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 27 Apr 2026 15:03:19 -0400 Subject: [PATCH] (completion--file-name-table): Improve completion of `/a/~b/c` The solution used is far from perfect, but hopefully it helps more often than it hurts. And since it affects only completion, we hope the cases where it hurts won't be too serious. (bug#32215) * lisp/minibuffer.el (completion--sifn-regardless-of-system-users): New function. (completion--file-name-table): Use it. --- lisp/minibuffer.el | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 94fc63440b4..636d0951645 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -3679,6 +3679,27 @@ same as `substitute-in-file-name'." all)))))) (file-error nil))) ;PCM often calls with invalid directories. +(defun completion--sifn-regardless-of-system-users (filename) + ;; `substitute-in-file-name' handles `~FOO' specially (by cutting + ;; any prefix to it that ends in `/', so `/foo/~bar/baz' turns into + ;; `~bar/baz') but only if `FOO' is the name of a user on the system. + ;; Completion of `~FOO' against the set of user names is handled + ;; already in `completion-file-name-table' when `~FOO' occurs at the + ;; beginning of the file name, but if we want to support such completions + ;; "in the middle", I see two ways to do it: + ;; - Do something similar to `completion--embedded-envvar-table'. + ;; - Extend `substitute-in-file-name's handling of `~FOO' so + ;; `/foo/~bar/baz' turns into `~bar/baz' regardless if `bar' + ;; is a valid user name (because we hope that it will be completed + ;; to a user name). + ;; The second option is what we do here. (bug#32215) + ;; FIXME: This breaks completion when you have a file or directory + ;; whose name starts with `~'. + ;; FIXME: This doesn't take file-name-handlers into account. + (while (string-match "/~[[:alnum:]_*-]" filename) + (setq filename (substring filename (1+ (match-beginning 0))))) + (substitute-in-file-name filename)) + (defun completion--sifn-requote (upos qstr) ;; We're looking for (the largest) `qpos' such that: ;; (equal (substring (substitute-in-file-name qstr) 0 upos) @@ -3761,7 +3782,10 @@ except that it passes the file name through `substitute-in-file-name'." (let ((table #'completion-file-name-table)) (if (eq (car-safe action) 'boundaries) (cons 'boundaries (completion--sifn-boundaries orig table pred (cdr action))) - (let* ((sifned (substitute-in-file-name orig)) + (let* ((sifned + (if (eq action 'lambda) + (substitute-in-file-name orig) + (completion--sifn-regardless-of-system-users orig))) (orig-start (car (completion--sifn-boundaries orig table pred ""))) (sifned-start (car (completion-boundaries sifned table pred ""))) (orig-in-bounds (substring orig orig-start))