aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDmitry Gutov2019-05-14 05:09:19 +0300
committerDmitry Gutov2019-05-14 05:11:18 +0300
commite0ee41d155b210327eb9c9ad5334f80ed59439f4 (patch)
tree563710e61b14362454a11c33fbb348c0066d9a7a
parent9b28a5083edecacfac3c7e16308bd8af3f4773a2 (diff)
downloademacs-e0ee41d155b210327eb9c9ad5334f80ed59439f4.tar.gz
emacs-e0ee41d155b210327eb9c9ad5334f80ed59439f4.zip
Allow customizing the display of project file names when reading
To hopefully resolve a long-running discussion (https://lists.gnu.org/archive/html/emacs-devel/2019-05/msg00162.html). * lisp/progmodes/project.el (project-read-file-name-function): New variable. (project--read-file-absolute, project--read-file-cpd-relative): New functions, possible values for the above. (project-find-file-in): Use the introduced variable. (project--completing-read-strict): Retain just the logic that fits the name.
-rw-r--r--etc/NEWS2
-rw-r--r--lisp/minibuffer.el2
-rw-r--r--lisp/progmodes/project.el95
3 files changed, 57 insertions, 42 deletions
diff --git a/etc/NEWS b/etc/NEWS
index 43ad8be1cc1..fa9ca8603de 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1983,6 +1983,8 @@ returns a regexp that never matches anything, which is an identity for
1983this operation. Previously, the empty string was returned in this 1983this operation. Previously, the empty string was returned in this
1984case. 1984case.
1985 1985
1986** New variable project-read-file-name-function.
1987
1986 1988
1987* Changes in Emacs 27.1 on Non-Free Operating Systems 1989* Changes in Emacs 27.1 on Non-Free Operating Systems
1988 1990
diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el
index dbd24dfa0a3..d11a5cf574d 100644
--- a/lisp/minibuffer.el
+++ b/lisp/minibuffer.el
@@ -846,6 +846,8 @@ styles for specific categories, such as files, buffers, etc."
846(defvar completion-category-defaults 846(defvar completion-category-defaults
847 '((buffer (styles . (basic substring))) 847 '((buffer (styles . (basic substring)))
848 (unicode-name (styles . (basic substring))) 848 (unicode-name (styles . (basic substring)))
849 ;; A new style that combines substring and pcm might be better,
850 ;; e.g. one that does not anchor to bos.
849 (project-file (styles . (substring))) 851 (project-file (styles . (substring)))
850 (info-menu (styles . (basic substring)))) 852 (info-menu (styles . (basic substring))))
851 "Default settings for specific completion categories. 853 "Default settings for specific completion categories.
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 7c8ca15868e..ddb4f3354cd 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -157,19 +157,13 @@ end it with `/'. DIR must be one of `project-roots' or
157 vc-directory-exclusion-list) 157 vc-directory-exclusion-list)
158 grep-find-ignored-files)) 158 grep-find-ignored-files))
159 159
160(cl-defgeneric project-file-completion-table (project dirs) 160(defun project--file-completion-table (all-files)
161 "Return a completion table for files in directories DIRS in PROJECT. 161 (lambda (string pred action)
162DIRS is a list of absolute directories; it should be some 162 (cond
163subset of the project roots and external roots. 163 ((eq action 'metadata)
164 164 '(metadata . ((category . project-file))))
165The default implementation delegates to `project-files'." 165 (t
166 (let ((all-files (project-files project dirs))) 166 (complete-with-action action all-files string pred)))))
167 (lambda (string pred action)
168 (cond
169 ((eq action 'metadata)
170 '(metadata . ((category . project-file))))
171 (t
172 (complete-with-action action all-files string pred))))))
173 167
174(cl-defmethod project-roots ((project (head transient))) 168(cl-defmethod project-roots ((project (head transient)))
175 (list (cdr project))) 169 (list (cdr project)))
@@ -470,55 +464,72 @@ recognized."
470 (project-external-roots pr)))) 464 (project-external-roots pr))))
471 (project-find-file-in (thing-at-point 'filename) dirs pr))) 465 (project-find-file-in (thing-at-point 'filename) dirs pr)))
472 466
467(defcustom project-read-file-name-function #'project--read-file-cpd-relative
468 "Function to call to read a file name from a list.
469For the arguments list, see `project--read-file-cpd-relative'."
470 :type '(repeat (choice (const :tag "Read with completion from relative names"
471 project--read-file-cpd-relative)
472 (const :tag "Read with completion from absolute names"
473 project--read-file-absolute)
474 (function :tag "custom function" nil))))
475
476(defun project--read-file-cpd-relative (prompt
477 all-files &optional predicate
478 hist default)
479 (let* ((common-parent-directory
480 (let ((common-prefix (try-completion "" all-files)))
481 (if (> (length common-prefix) 0)
482 (file-name-directory common-prefix))))
483 (cpd-length (length common-parent-directory))
484 (prompt (if (zerop cpd-length)
485 prompt
486 (concat prompt (format " in %s" common-parent-directory))))
487 (substrings (mapcar (lambda (s) (substring s cpd-length)) all-files))
488 (new-collection (project--file-completion-table substrings))
489 (res (project--completing-read-strict prompt
490 new-collection
491 predicate
492 hist default)))
493 (concat common-parent-directory res)))
494
495(defun project--read-file-absolute (prompt
496 all-files &optional predicate
497 hist default)
498 (project--completing-read-strict prompt
499 (project--file-completion-table all-files)
500 predicate
501 hist default))
502
473(defun project-find-file-in (filename dirs project) 503(defun project-find-file-in (filename dirs project)
474 "Complete FILENAME in DIRS in PROJECT and visit the result." 504 "Complete FILENAME in DIRS in PROJECT and visit the result."
475 (let* ((table (project-file-completion-table project dirs)) 505 (let* ((all-files (project-files project dirs))
476 (file (project--completing-read-strict 506 (file (funcall project-read-file-name-function
477 "Find file" table nil nil 507 "Find file" all-files nil nil
478 filename))) 508 filename)))
479 (if (string= file "") 509 (if (string= file "")
480 (user-error "You didn't specify the file") 510 (user-error "You didn't specify the file")
481 (find-file file)))) 511 (find-file file))))
482 512
483(defun project--completing-read-strict (prompt 513(defun project--completing-read-strict (prompt
484 collection &optional predicate 514 collection &optional predicate
485 hist default inherit-input-method) 515 hist default)
486 ;; Tried both expanding the default before showing the prompt, and 516 ;; Tried both expanding the default before showing the prompt, and
487 ;; removing it when it has no matches. Neither seems natural 517 ;; removing it when it has no matches. Neither seems natural
488 ;; enough. Removal is confusing; early expansion makes the prompt 518 ;; enough. Removal is confusing; early expansion makes the prompt
489 ;; too long. 519 ;; too long.
490 (let* ((common-parent-directory 520 (let* ((new-prompt (if default
491 (let ((common-prefix (try-completion "" collection)))
492 (if (> (length common-prefix) 0)
493 (file-name-directory common-prefix))))
494 (cpd-length (length common-parent-directory))
495 (prompt (if (zerop cpd-length)
496 prompt
497 (concat prompt (format " in %s" common-parent-directory))))
498 ;; XXX: This requires collection to be "flat" as well.
499 (substrings (mapcar (lambda (s) (substring s cpd-length))
500 (all-completions "" collection)))
501 (new-collection
502 (lambda (string pred action)
503 (cond
504 ((eq action 'metadata)
505 (if (functionp collection) (funcall collection nil nil 'metadata)))
506 (t
507 (complete-with-action action substrings string pred)))))
508 (new-prompt (if default
509 (format "%s (default %s): " prompt default) 521 (format "%s (default %s): " prompt default)
510 (format "%s: " prompt))) 522 (format "%s: " prompt)))
511 (res (completing-read new-prompt 523 (res (completing-read new-prompt
512 new-collection predicate t 524 collection predicate t
513 nil ;; initial-input 525 nil ;; initial-input
514 hist default inherit-input-method))) 526 hist default)))
515 (when (and (equal res default) 527 (when (and (equal res default)
516 (not (test-completion res collection predicate))) 528 (not (test-completion res collection predicate)))
517 (setq res 529 (setq res
518 (completing-read (format "%s: " prompt) 530 (completing-read (format "%s: " prompt)
519 new-collection predicate t res hist nil 531 collection predicate t res hist nil)))
520 inherit-input-method))) 532 res))
521 (concat common-parent-directory res)))
522 533
523(declare-function fileloop-continue "fileloop" ()) 534(declare-function fileloop-continue "fileloop" ())
524 535