aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorDmitry Gutov2019-05-14 05:09:19 +0300
committerDmitry Gutov2019-05-14 05:11:18 +0300
commite0ee41d155b210327eb9c9ad5334f80ed59439f4 (patch)
tree563710e61b14362454a11c33fbb348c0066d9a7a /lisp
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.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/minibuffer.el2
-rw-r--r--lisp/progmodes/project.el95
2 files changed, 55 insertions, 42 deletions
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