diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el index 7c8ca15868..70c9f60a0d 100644 --- a/lisp/progmodes/project.el +++ b/lisp/progmodes/project.el @@ -157,19 +157,13 @@ project--find-in-directory vc-directory-exclusion-list) grep-find-ignored-files)) -(cl-defgeneric project-file-completion-table (project dirs) - "Return a completion table for files in directories DIRS in PROJECT. -DIRS is a list of absolute directories; it should be some -subset of the project roots and external roots. - -The default implementation delegates to `project-files'." - (let ((all-files (project-files project dirs))) - (lambda (string pred action) - (cond - ((eq action 'metadata) - '(metadata . ((category . project-file)))) - (t - (complete-with-action action all-files string pred)))))) +(defun project-file--completion-table (all-files) + (lambda (string pred action) + (cond + ((eq action 'metadata) + '(metadata . ((category . project-file)))) + (t + (complete-with-action action all-files string pred))))) (cl-defmethod project-roots ((project (head transient))) (list (cdr project))) @@ -470,19 +464,14 @@ project-or-external-find-file (project-external-roots pr)))) (project-find-file-in (thing-at-point 'filename) dirs pr))) -(defun project-find-file-in (filename dirs project) - "Complete FILENAME in DIRS in PROJECT and visit the result." - (let* ((table (project-file-completion-table project dirs)) - (file (project--completing-read-strict - "Find file" table nil nil - filename))) - (if (string= file "") - (user-error "You didn't specify the file") - (find-file file)))) +(defcustom project-find-file-read-fn #'project-find-file--read-cpd-relative + "Function to call to read a file name from a list. +For the arguments list, see project-find-file--read-cpd-relative." + :type 'function) -(defun project--completing-read-strict (prompt - collection &optional predicate - hist default inherit-input-method) +(defun project-find-file--read-cpd-relative (prompt + collection &optional predicate + hist default) ;; Tried both expanding the default before showing the prompt, and ;; removing it when it has no matches. Neither seems natural ;; enough. Removal is confusing; early expansion makes the prompt @@ -504,21 +493,43 @@ project--completing-read-strict ((eq action 'metadata) (if (functionp collection) (funcall collection nil nil 'metadata))) (t - (complete-with-action action substrings string pred))))) - (new-prompt (if default + (complete-with-action action substrings string pred))))) + (res (project--completing-read-strict prompt + new-collection predicate + hist default))) + (concat common-parent-directory res))) + +(defun project-find-file-in (filename dirs project) + "Complete FILENAME in DIRS in PROJECT and visit the result." + (let* ((all-files (project-files project dirs)) + (table (project-file--completion-table all-files)) + (file (funcall project-find-file-read-fn + "Find file" table nil nil + filename))) + (if (string= file "") + (user-error "You didn't specify the file") + (find-file file)))) + +(defun project--completing-read-strict (prompt + collection &optional predicate + hist default) + ;; Tried both expanding the default before showing the prompt, and + ;; removing it when it has no matches. Neither seems natural + ;; enough. Removal is confusing; early expansion makes the prompt + ;; too long. + (let* ((new-prompt (if default (format "%s (default %s): " prompt default) (format "%s: " prompt))) (res (completing-read new-prompt - new-collection predicate t + collection predicate t nil ;; initial-input - hist default inherit-input-method))) + hist default))) (when (and (equal res default) (not (test-completion res collection predicate))) (setq res (completing-read (format "%s: " prompt) - new-collection predicate t res hist nil - inherit-input-method))) - (concat common-parent-directory res))) + collection predicate t res hist nil))) + res)) (declare-function fileloop-continue "fileloop" ())