From: Eshel Yaron Date: Thu, 4 Jan 2024 20:09:53 +0000 (+0100) Subject: Improve handling of file name completion predicate X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=e49331cd5be46cb69933c06e7bb1020a1eba0e38;p=emacs.git 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. --- diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index b565d4f095b..0c0365ca74c 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 2722656c418..c288e421206 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -3431,11 +3431,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)) @@ -3448,26 +3443,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. @@ -3643,9 +3629,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 196c5f159cd..c6669c8d1db 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)))) ;; `completion-file-name-table' calls `file-exists-p' when ;; the predicate is nil.