From 3b58baa4d2eb2158e64bf2b135e7a46330113fcf Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Thu, 4 Jan 2024 21:09:53 +0100 Subject: [PATCH] Improve handling of file name completion predicate * lisp/minibuffer.el (completion-file-name-table): Avoid hard-coding 'file-exists-p', call predicate without directory name and set 'default-directory' to that directory instead. (read-file-name): Update documentation. * lisp/pcomplete.el (pcomplete--entries): Handle zero-length arg. * doc/lispref/minibuf.texi (Reading File Names): Elaborate. --- doc/lispref/minibuf.texi | 4 +++- lisp/minibuffer.el | 42 +++++++++++++++------------------------- lisp/pcomplete.el | 9 +++++---- 3 files changed, 24 insertions(+), 31 deletions(-) diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index d8f3ca02a0f..2bd600fbd3c 100644 --- a/doc/lispref/minibuf.texi +++ b/doc/lispref/minibuf.texi @@ -1774,7 +1774,9 @@ current buffer visit no file using @kbd{M-x set-visited-file-name}. If @var{predicate} is non-@code{nil}, it specifies a function of one argument that decides which file names are acceptable completion alternatives. A file name is an acceptable value if @var{predicate} -returns non-@code{nil} for it. +returns non-@code{nil} for it. If @var{predicate} is @code{nil}, +@code{read-file-name} uses @code{file-exists-p}, so acceptable +completions are existing file names. Here is an example of using @code{read-file-name}: diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 2e77d9968ec..1ea69674cc8 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -3357,11 +3357,6 @@ same as `substitute-in-file-name'." ;; into more such problematic cases. ,(min start (length string)) . ,end))) - ((eq action 'lambda) - (if (zerop (length string)) - nil ;Not sure why it's here, but it probably doesn't harm. - (funcall (or pred 'file-exists-p) string))) - (t (let* ((name (file-name-nondirectory string)) (specdir (file-name-directory string)) @@ -3374,26 +3369,17 @@ same as `substitute-in-file-name'." (concat specdir comp) comp))) + ((eq action 'lambda) + (or (not pred) + (let ((default-directory (expand-file-name realdir))) + (funcall pred name)))) + ((eq action t) (let ((all (file-name-all-completions name realdir))) - - ;; Check the predicate, if necessary. - (unless (memq pred '(nil file-exists-p)) - (let ((comp ()) - (pred - (if (eq pred 'file-directory-p) - ;; Brute-force speed up for directory checking: - ;; Discard strings which don't end in a slash. - (lambda (s) - (let ((len (length s))) - (and (> len 0) (eq (aref s (1- len)) ?/)))) - ;; Must do it the hard (and slow) way. - pred))) - (let ((default-directory (expand-file-name realdir))) - (dolist (tem all) - (if (funcall pred tem) (push tem comp)))) - (setq all (nreverse comp)))) - + (when pred + (setq all + (let ((default-directory (expand-file-name realdir))) + (seq-filter pred all)))) all)))))) (file-error nil))) ;PCM often calls with invalid directories. @@ -3569,9 +3555,13 @@ full file name for INITIAL will usually lead to surprising results. Sixth arg PREDICATE, if non-nil, should be a function of one -argument; then a file name is considered an acceptable completion -alternative only if PREDICATE returns non-nil with the file name -as its argument. +argument; then a file name is considered existing (and hence an +acceptable completion alternative) only if PREDICATE returns +non-nil with the file name as its argument. PREDICATE is called +with `default-directory' bound to the direcotry part of the +candidate file name, while the argument passed to PREDICATE does +not include a directory part. If PREDICATE is omitted or nil, it +defaults to `file-exists-p'. If this command was invoked with the mouse, use a graphical file dialog if `use-dialog-box' is non-nil, and the window system or X diff --git a/lisp/pcomplete.el b/lisp/pcomplete.el index 564006043e5..46ec67d5de6 100644 --- a/lisp/pcomplete.el +++ b/lisp/pcomplete.el @@ -897,10 +897,11 @@ this is `comint-dynamic-complete-functions'." (let ((file-ignore pcomplete-file-ignore) (dir-ignore pcomplete-dir-ignore)) (lambda (file) - (not - (if (eq (aref file (1- (length file))) ?/) - (and dir-ignore (string-match dir-ignore file)) - (and file-ignore (string-match file-ignore file)))))))) + (and (< 0 (length file)) + (not + (if (eq (aref file (1- (length file))) ?/) + (and dir-ignore (string-match dir-ignore file)) + (and file-ignore (string-match file-ignore file))))))))) (reg-pred (if regexp (lambda (file) (string-match regexp file)))) (pred (cond ((null (or ign-pred reg-pred)) predicate) -- 2.39.5