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

    Highlight the tool and default value in the search prompts
    
    The search prompts now make two things obvious at a glance: which tool
    will run and what the default search value is.
    
    - projectile-search/grep/ag/ripgrep face the backend name (a new
      projectile-search-prompt-tool face) and the symbol-at-point default (a
      new projectile-search-prompt-default face, shown as "(default X)").
    - The reviewable search grew the same treatment and now advertises
      whether the literal search will take the ripgrep fast-path or the
      elisp scan ([ripgrep] vs [elisp]), and shows its default up front
      instead of only pre-filling it.
---
 CHANGELOG.md                          |  1 +
 projectile.el                         | 60 +++++++++++++++++++++++++++--------
 test/projectile-search-review-test.el | 22 +++++++++++++
 test/projectile-search-test.el        | 32 +++++++++++++++++++
 4 files changed, 102 insertions(+), 13 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index cd989ff4d4..6f0a14968a 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).
+* 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  [...]
 
 ### Bugs fixed
 
diff --git a/projectile.el b/projectile.el
index 77ce578503..9b2ca2e05b 100644
--- a/projectile.el
+++ b/projectile.el
@@ -7012,14 +7012,39 @@ This is a subset of `grep-read-files', where either a 
matching entry from
   "Return ignored file suffixes as a list of glob patterns."
   (mapcar (lambda (pat) (concat "*" pat)) 
projectile-globally-ignored-file-suffixes))
 
-(defun projectile--read-search-string-with-default (prefix-label)
-  (let* ((prefix-label (projectile-prepend-project-name prefix-label))
+(defface projectile-search-prompt-tool
+  '((t :inherit font-lock-function-name-face :weight bold))
+  "Face for the tool/backend highlighted in Projectile's search prompts."
+  :group 'projectile
+  :package-version '(projectile . "3.2.0"))
+
+(defface projectile-search-prompt-default
+  '((t :inherit font-lock-constant-face))
+  "Face for the default value highlighted in Projectile's search prompts."
+  :group 'projectile
+  :package-version '(projectile . "3.2.0"))
+
+(defun projectile--search-tool-tag (tool)
+  "Return a faced `[TOOL]' tag for a search prompt.
+Used where the backend varies (e.g. `projectile-search' or the
+reviewable search's ripgrep/elisp fast-path) so the prompt makes clear
+which tool will run."
+  (format "[%s]" (propertize tool 'face 'projectile-search-prompt-tool)))
+
+(defun projectile--read-search-string-with-default (prompt-label)
+  "Read a search string, defaulting to the symbol or region at point.
+PROMPT-LABEL is the action shown in the prompt (already carrying any
+faced tool tag its caller wants).  The default value, when there is one,
+is appended and faced as `(default VALUE)'."
+  (let* ((prompt-label (projectile-prepend-project-name prompt-label))
          (default-value (projectile-symbol-or-selection-at-point))
          (default-label (if (or (not default-value)
                                 (string= default-value ""))
                             ""
-                          (format " (default %s)" default-value))))
-    (read-string (format "%s%s: " prefix-label default-label) nil nil 
default-value)))
+                          (format " (default: %s)"
+                                  (propertize default-value
+                                              'face 
'projectile-search-prompt-default)))))
+    (read-string (format "%s%s: " prompt-label default-label) nil nil 
default-value)))
 
 (defvar projectile-grep-find-ignored-paths)
 (defvar projectile-grep-find-unignored-paths)
@@ -7353,8 +7378,9 @@ The backend is chosen from `projectile-search-backends' 
according to
                                                 "search"))
          (term (or search-term
                    (projectile--read-search-string-with-default
-                    (format "Search [%s]%s for"
-                            (car backend) (if regexp " regexp" ""))))))
+                    (format "Search %s%s for"
+                            (projectile--search-tool-tag (car backend))
+                            (if regexp " regexp" ""))))))
     (funcall (plist-get (cdr backend) :search) term regexp)))
 
 ;;;###autoload
@@ -7370,7 +7396,9 @@ With REGEXP given, don't query the user for a regexp."
   ;; Fail fast (with a friendly error) before prompting when not in a project.
   (projectile-acquire-root)
   (let ((search-regexp (or regexp
-                           (projectile--read-search-string-with-default "Grep 
for")))
+                           (projectile--read-search-string-with-default
+                            (format "Search %s for"
+                                    (projectile--search-tool-tag "grep")))))
         (files (and arg (or (and (equal current-prefix-arg '-)
                                  (projectile-grep-default-files))
                             (read-string (projectile-prepend-project-name 
"Grep in: ")
@@ -7386,7 +7414,9 @@ With an optional prefix argument ARG SEARCH-TERM is 
interpreted as a
 regular expression."
   (interactive
    (list (projectile--read-search-string-with-default
-          (format "Ag %ssearch for" (if current-prefix-arg "regexp " "")))
+          (format "Search %s%s for"
+                  (projectile--search-tool-tag "ag")
+                  (if current-prefix-arg " regexp" "")))
          current-prefix-arg))
   (let ((projectile-search-backend 'ag))
     (projectile-search search-term arg)))
@@ -7403,7 +7433,9 @@ This command depends on the Emacs packages ripgrep or rg 
being
 installed to work."
   (interactive
    (list (projectile--read-search-string-with-default
-          (format "Ripgrep %ssearch for" (if current-prefix-arg "regexp " "")))
+          (format "Search %s%s for"
+                  (projectile--search-tool-tag "ripgrep")
+                  (if current-prefix-arg " regexp" "")))
          current-prefix-arg))
   (let ((projectile-search-backend 'ripgrep))
     (projectile-search search-term arg)))
@@ -9587,10 +9619,12 @@ matches to a `grep-mode' buffer for wgrep or Emacs 31's 
`grep-edit-mode'.
 LITERAL non-nil searches for a literal string; otherwise the term is an
 Emacs regexp.  There is no replacement prompt."
   (let* ((root (projectile-acquire-root))
-         (term (read-string
-                (projectile-prepend-project-name
-                 (if literal "Search: " "Search regexp: "))
-                (projectile-symbol-or-selection-at-point)))
+         (term (projectile--read-search-string-with-default
+                (format "Search %s%s for"
+                        (projectile--search-tool-tag
+                         (if (and literal (projectile-search--rg-fastpath-p t))
+                             "ripgrep" "elisp"))
+                        (if literal "" " regexp"))))
          (regexp (if literal (regexp-quote term) term))
          (case-fold case-fold-search)
          (candidates (projectile-replace--candidates term literal case-fold 
root)))
diff --git a/test/projectile-search-review-test.el 
b/test/projectile-search-review-test.el
index 8ba419978a..aae37d1445 100644
--- a/test/projectile-search-review-test.el
+++ b/test/projectile-search-review-test.el
@@ -89,6 +89,28 @@ REGEXP-P selects `projectile-search-regexp-review'."
       (let ((buf (projectile-search-review-test--run "absent")))
         (expect buf :to-be nil)))))
 
+(describe "projectile-search-review prompt tool tag"
+  (before-each
+    (spy-on 'projectile-acquire-root :and-return-value "/proj/")
+    (spy-on 'projectile-replace--candidates :and-return-value nil)
+    (spy-on 'projectile-replace--open))
+
+  (it "advertises [ripgrep] when the literal fast-path will run"
+    (spy-on 'projectile-search--rg-fastpath-p :and-return-value t)
+    (let (label)
+      (spy-on 'projectile--read-search-string-with-default :and-call-fake
+              (lambda (l) (setq label l) "foo"))
+      (projectile-search--review t)
+      (expect label :to-match (regexp-quote "[ripgrep]"))))
+
+  (it "advertises [elisp] for a regexp search that never takes the fast-path"
+    (spy-on 'projectile-search--rg-fastpath-p :and-return-value nil)
+    (let (label)
+      (spy-on 'projectile--read-search-string-with-default :and-call-fake
+              (lambda (l) (setq label l) "foo"))
+      (projectile-search--review nil)
+      (expect label :to-match (regexp-quote "[elisp]")))))
+
 (describe "projectile-search-regexp-review"
   (it "honors Emacs-only regexp constructs a shell regexp couldn't express"
     (projectile-test-with-project
diff --git a/test/projectile-search-test.el b/test/projectile-search-test.el
index 7ec218b29d..397f5e359a 100644
--- a/test/projectile-search-test.el
+++ b/test/projectile-search-test.el
@@ -87,6 +87,38 @@
       (projectile-search "foo" t)
       (expect captured :to-equal '("foo" t)))))
 
+(describe "search prompt highlighting"
+  (it "wraps the tool in brackets and faces it"
+    (let ((tag (projectile--search-tool-tag "ripgrep")))
+      ;; face aside, it's a plain [tool] tag
+      (expect tag :to-equal "[ripgrep]")
+      (expect (get-text-property 1 'face tag)
+              :to-equal 'projectile-search-prompt-tool)))
+
+  (describe "projectile--read-search-string-with-default"
+    (before-each
+      (spy-on 'projectile-prepend-project-name :and-call-fake #'identity))
+
+    (it "shows the symbol-at-point default, faced, and returns it on RET"
+      (spy-on 'projectile-symbol-or-selection-at-point :and-return-value "foo")
+      (let (prompt)
+        (spy-on 'read-string :and-call-fake
+                (lambda (p &rest _) (setq prompt p) "foo"))
+        (expect (projectile--read-search-string-with-default "Grep for") 
:to-equal "foo")
+        (expect prompt :to-match (regexp-quote "(default: foo)"))
+        ;; the default value itself carries the highlight face
+        (let ((i (string-match "foo)" prompt)))
+          (expect (get-text-property i 'face prompt)
+                  :to-equal 'projectile-search-prompt-default))))
+
+    (it "omits the default label when there is no symbol at point"
+      (spy-on 'projectile-symbol-or-selection-at-point :and-return-value nil)
+      (let (prompt)
+        (spy-on 'read-string :and-call-fake
+                (lambda (p &rest _) (setq prompt p) "typed"))
+        (projectile--read-search-string-with-default "Grep for")
+        (expect prompt :not :to-match "default")))))
+
 (describe "the search wrapper commands"
   (it "projectile-ripgrep forces the ripgrep backend"
     (let (seen)

Reply via email to