branch: elpa/projectile
commit 095f493b6e9091d41247d2d4a741c84f9fc8158c
Author: Bozhidar Batsov <[email protected]>
Commit: Bozhidar Batsov <[email protected]>

    Advertise the correct completion category per command
    
    projectile-completing-read hardcoded the `project-file' completion
    category for every prompt, so marginalia and embark mis-annotated
    non-file completions (a buffer or a project got file annotations and
    file actions).
    
    Add a :category keyword (default project-file) and pass the right one:
    buffer switching now advertises `buffer' (marginalia's rich buffer
    annotations kick in), and the project-switch commands advertise `file'
    since their candidates are directory paths. File commands keep the
    project-file default.
---
 CHANGELOG.md                 |  1 +
 projectile.el                | 29 ++++++++++++++++++++++-------
 test/projectile-core-test.el | 37 ++++++++++++++++++++++++++++++++++++-
 3 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index aaff9b9606..11a61f2f15 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -43,6 +43,7 @@
 
 * Add `projectile-frecency-max-projects` (default 100), which caps how many 
projects' frecency history is kept so the store can't grow without bound.
 * `projectile-replace-scan-chunk-size` is now a user-facing `defcustom` (was 
an internal variable).
+* Advertise the right completion metadata category per command so marginalia 
and embark annotate and act on candidates correctly: buffer switching now uses 
the `buffer` category (so marginalia shows its rich buffer annotations), 
project switching uses `file`, and file commands keep `project-file` - 
previously every completion was hardcoded to `project-file`. 
`projectile-completing-read` gained a `:category` keyword for this.
 * Add whole-word matching to the reviewable search/replace: toggle it with `w` 
in the results buffer, default it with `projectile-search-whole-word`, or seed 
it for one run with the `projectile-dispatch` `--word` switch. The elisp scan 
fences the pattern with word boundaries and the ripgrep fast-path passes 
`--word-regexp`, so both engines agree.
 * Wire the reviewable search/replace into the `projectile-dispatch` Modifiers: 
the two literal/regexp search entries are folded into one `sR` driven by 
`--regexp` (matching how `--regexp` already drives the ag/ripgrep search), and 
a new `--case-sensitive` switch seeds the reviewable search/replace 
case-sensitive. Both can still be flipped with `x`/`c` in the results buffer.
 * Highlight the search tool and the default value in the search prompts, and 
give them one consistent `Search [tool] for (default: X)` format across 
`projectile-search`, `projectile-grep`/`-ag`/`-ripgrep` and the reviewable 
search. The tool that will run (`grep`/`ag`/`ripgrep`, or `ripgrep`/`elisp` for 
the reviewable search's literal fast-path) is faced with 
`projectile-search-prompt-tool` and the symbol-at-point default with 
`projectile-search-prompt-default`. The reviewable search now  [...]
diff --git a/projectile.el b/projectile.el
index b42a779b8c..30a48f7d8a 100644
--- a/projectile.el
+++ b/projectile.el
@@ -2739,6 +2739,7 @@ See also `projectile-acquire-root'."
     (cond
      ((eq projectile-require-project-root 'prompt) (projectile-completing-read
                                                     "Switch to project: " 
projectile-known-projects
+                                                    :category 'file
                                                     :caller 
'projectile-read-project))
      (projectile-require-project-root (user-error "Projectile cannot find a 
project definition in %s" default-directory))
      (t default-directory))))
@@ -3902,6 +3903,7 @@ choices."
    prompt
    (delete (buffer-name (current-buffer))
            (projectile-project-buffer-names))
+   :category 'buffer
    :caller 'projectile-read-buffer))
 
 ;;; Other window/frame display variants
@@ -3990,6 +3992,7 @@ yields the other-window/-frame variants."
    (projectile-completing-read
     "Display buffer: "
     (projectile-project-buffer-names)
+    :category 'buffer
     :caller 'projectile-read-buffer)))
 
 ;;;###autoload
@@ -4365,7 +4368,7 @@ Never use on many files since it's going to recalculate 
the
 project-root for every file."
   (expand-file-name name (projectile-project-root dir)))
 
-(cl-defun projectile-completing-read (prompt choices &key initial-input action 
caller sort-function)
+(cl-defun projectile-completing-read (prompt choices &key initial-input action 
caller sort-function (category 'project-file))
   "Present a project tailored PROMPT with CHOICES.
 
 Reads with `completing-read', unless `projectile-completion-system' is a
@@ -4375,8 +4378,15 @@ INITIAL-INPUT is passed to `completing-read'.  ACTION, 
when non-nil, is
 called on the selected candidate and its result returned.  SORT-FUNCTION,
 when non-nil, is exposed as the completion metadata's
 `display-sort-function' and `cycle-sort-function', so completion UIs
-that honor metadata present the candidates in that order.  CALLER is
-accepted for backward compatibility but no longer used."
+that honor metadata present the candidates in that order.
+
+CATEGORY is the completion metadata category advertised to UIs like
+marginalia and embark so they annotate and act on the candidates
+appropriately; it defaults to `project-file' (the candidates are project
+files) and should be overridden when they are not - e.g. `buffer' for a
+buffer switch or `file' for a directory.  A nil CATEGORY omits it.
+
+CALLER is accepted for backward compatibility but no longer used."
   (ignore caller)
   (let* ((prompt (projectile-prepend-project-name prompt))
          (res (if (functionp projectile-completion-system)
@@ -4384,11 +4394,10 @@ accepted for backward compatibility but no longer used."
                 (completing-read
                  prompt
                  (lambda (string pred action)
-                   ;; The `project-file' category lets packages like
-                   ;; marginalia and embark enhance how candidates are
-                   ;; presented.
+                   ;; The completion category lets packages like marginalia
+                   ;; and embark enhance how candidates are presented.
                    (if (eq action 'metadata)
-                       `(metadata (category . project-file)
+                       `(metadata ,@(when category `((category . ,category)))
                                   ,@(when sort-function
                                       `((display-sort-function . 
,sort-function)
                                         (cycle-sort-function . 
,sort-function))))
@@ -9818,6 +9827,7 @@ prompt for a known project to open instead of the current 
one."
            (if arg
                (projectile-completing-read
                 "Dired in project: " (projectile-relevant-known-projects)
+                :category 'file
                 :caller 'projectile-read-project)
              (projectile-acquire-root))))
 
@@ -9851,6 +9861,7 @@ directory to open."
                      (projectile-completing-read
                       "Open project VC in: "
                       projectile-known-projects
+                      :category 'file
                       :caller 'projectile-read-project))))
   (unless project-root
     (setq project-root (projectile-acquire-root)))
@@ -11024,6 +11035,7 @@ of `projectile-switch-project-action'."
          "Switch to project: " projects
          :action (lambda (project)
                    (projectile-switch-project-by-name project arg))
+         :category 'file
          :caller 'projectile-read-project)
       (user-error "There are no known projects"))))
 
@@ -11040,6 +11052,7 @@ of `projectile-switch-project-action'."
          "Switch to open project: " projects
          :action (lambda (project)
                    (projectile-switch-project-by-name project arg))
+         :category 'file
          :caller 'projectile-read-project)
       (user-error "There are no open projects"))))
 
@@ -11317,6 +11330,7 @@ projects removed."
   (interactive (list (projectile-completing-read
                       "Remove from known projects: " projectile-known-projects
                       :action 'projectile-remove-known-project
+                      :category 'file
                       :caller 'projectile-read-project)))
   (unless (called-interactively-p 'any)
     (setq projectile-known-projects
@@ -11447,6 +11461,7 @@ Let user choose another project when PROMPT-FOR-PROJECT 
is supplied."
                           (projectile-completing-read
                            "Project name: "
                            (projectile-relevant-known-projects)
+                           :category 'file
                            :caller 'projectile-read-project)
                         (projectile-acquire-root))))
     (projectile-ibuffer-by-project project-root)))
diff --git a/test/projectile-core-test.el b/test/projectile-core-test.el
index ee9266b0b4..c849046558 100644
--- a/test/projectile-core-test.el
+++ b/test/projectile-core-test.el
@@ -279,7 +279,42 @@
     (let ((projectile-completion-system 'ido))
       (spy-on 'completing-read :and-return-value "x")
       (expect (projectile-completing-read "Prompt" '("x")) :to-equal "x")
-      (expect 'completing-read :to-have-been-called))))
+      (expect 'completing-read :to-have-been-called)))
+
+  ;; The completion metadata category is what marginalia and embark key off
+  ;; to annotate and act on candidates, so it must reflect what they are.
+  (it "advertises the project-file category by default"
+    (let ((projectile-completion-system 'default) table)
+      (spy-on 'completing-read :and-call-fake
+              (lambda (_prompt coll &rest _) (setq table coll) "x"))
+      (projectile-completing-read "Prompt" '("a"))
+      (expect (alist-get 'category (cdr (funcall table "" nil 'metadata)))
+              :to-be 'project-file)))
+
+  (it "honors an explicit :category"
+    (let ((projectile-completion-system 'default) table)
+      (spy-on 'completing-read :and-call-fake
+              (lambda (_prompt coll &rest _) (setq table coll) "x"))
+      (projectile-completing-read "Prompt" '("a") :category 'buffer)
+      (expect (alist-get 'category (cdr (funcall table "" nil 'metadata)))
+              :to-be 'buffer)))
+
+  (it "omits the category when :category is nil"
+    (let ((projectile-completion-system 'default) table)
+      (spy-on 'completing-read :and-call-fake
+              (lambda (_prompt coll &rest _) (setq table coll) "x"))
+      (projectile-completing-read "Prompt" '("a") :category nil)
+      (expect (assq 'category (cdr (funcall table "" nil 'metadata)))
+              :to-be nil)))
+
+  (it "buffer switching advertises the buffer category"
+    (let (table)
+      (spy-on 'projectile-project-buffer-names :and-return-value '("b1" "b2"))
+      (spy-on 'completing-read :and-call-fake
+              (lambda (_prompt coll &rest _) (setq table coll) "b1"))
+      (projectile-read-buffer-to-switch "Switch: ")
+      (expect (alist-get 'category (cdr (funcall table "" nil 'metadata)))
+              :to-be 'buffer))))
 
 (describe "projectile-sort-files"
   (it "returns the files unchanged for the default sort order"

Reply via email to