cp-find-file: make matcher customizable and provide basename matcher

Closes #72
This commit is contained in:
Eric Danan 2018-02-04 15:50:20 +01:00
parent e8eee886b7
commit 222b6ac531
2 changed files with 73 additions and 4 deletions

View file

@ -224,6 +224,10 @@ Templates contexts are read from the variable `counsel-projectile-org-capture-te
By default, when calling `counsel-projectile-switch-project`, the current project (if any) is included in the candidates list and preselected. Similarly, when calling `counsel-projectile-switch-to-buffer`, the current buffer is included in the candidates list and preselected. If you prefer removing these elements from the candidate lists of these commands, you can set the variables `counsel-projectile-remove-current-project` and `counsel-projectile-remove-current-buffer` accordingly. By default, when calling `counsel-projectile-switch-project`, the current project (if any) is included in the candidates list and preselected. Similarly, when calling `counsel-projectile-switch-to-buffer`, the current buffer is included in the candidates list and preselected. If you prefer removing these elements from the candidate lists of these commands, you can set the variables `counsel-projectile-remove-current-project` and `counsel-projectile-remove-current-buffer` accordingly.
## Initial input for the project search commands ## Initial input for the project search commands
If you want some initial input to be inserted in the minibuffer every time you call `counsel-projectile-grep`, `counsel-projectile-ag`, or `counsel-projectile-rg`, you can customize the variables `counsel-projectile-grep-initial-input`, `counsel-projectile-ag-initial-input`, or `counsel-projectile-rg-initial-input` accordingly. Each of these variable, if non `nil`, should hold a Lisp expression whose evaluation yields the initial input string. If you use the Customize interface, some choices are proposed based on various versions of the `thing-at-point` function. Note that you can always insert the value of `(ivy-thing-at-point)` by hitting <kbd>M-n</kbd> in the minibuffer. If you want some initial input to be inserted in the minibuffer every time you call `counsel-projectile-grep`, `counsel-projectile-ag`, or `counsel-projectile-rg`, you can customize the variables `counsel-projectile-grep-initial-input`, `counsel-projectile-ag-initial-input`, or `counsel-projectile-rg-initial-input` accordingly. Each of these variable, if non `nil`, should hold a Lisp expression whose evaluation yields the initial input string. If you use the Customize interface, some choices are proposed based on various versions of the `thing-at-point` function. Note that you can always insert the value of `(ivy-thing-at-point)` by hitting <kbd>M-n</kbd> in the minibuffer.
## Matcher for `counsel-projectile-find-file`
By default, the command `counsel-projectile-find-file` relies on the the matcher of the command `counsel-find-file` to display files matching minibuffer input, allowing to ignore some files based on the variable `counsel-find-file-ignore-regexp`. It is possible to use another matcher by setting the variable `counsel-projectile-find-file-matcher`. Some choices are proposed if you use the Customize interface, in particular the `counsel-projectile-find-file-matcher-basename` matcher which is provided by counsel-projectile and only displays files whose basename matches the minibuffer input (if there is none, it shows all matching files).
The matcher specified by `counsel-find-file-ignore-regexp` is also used by `counsel-projectile` to match files.
## Sorting candidates ## Sorting candidates
The following commands allow to modify the way candidates are sorted: The following commands allow to modify the way candidates are sorted:
- `counsel-projectile` - `counsel-projectile`

View file

@ -244,6 +244,35 @@ If anything goes wrong, throw an error and do not modify ACTION-VAR."
;;;; counsel-projectile-find-file ;;;; counsel-projectile-find-file
(defcustom counsel-projectile-find-file-matcher 'counsel--find-file-matcher
"Function returning candidates matching minibuffer input in
`counsel-projectile-find-file', also used to match files in
`counsel-projectile'.
Several choices are proposed:
- Ivy generic matcher (`ivy--re-filter'). This is the matcher
used by default in all ivy commands.
- Counsel matcher (`counsel--find-file-matcher'). This is the
matcher used in `counsel-find-file', allowing to ignore some
files based on `counsel-find-file-ignore-regexp'.
- Counsel-projectile basename
matcher (`counsel-projectile-basename-matcher'). This one only
displays files whose basename matches minibuffer input, or if
there is none all files whose name (relative to the project
root) matches. It also uses the counsel matcher to ignore some
files.
It is also possible to use a custom matcher. It must be a function taking two argument, the regexp and the candidates (see e.g. `counsel--find-file-matcher')."
:type '(choice
(const :tag "Ivy generic matcher" ivy--re-filter)
(const :tag "Counsel matcher" counsel--find-file-matcher)
(const :tag "Counsel-projectile basename matcher" counsel-projectile-find-file-matcher-basename)
(function :tag "Custom function"))
:group 'counsel-projectile)
(counsel-projectile--defcustom-action (counsel-projectile--defcustom-action
'counsel-projectile-find-file 'counsel-projectile-find-file
'(1 '(1
@ -261,6 +290,41 @@ If anything goes wrong, throw an error and do not modify ACTION-VAR."
"switch project")) "switch project"))
'counsel-projectile) 'counsel-projectile)
(defun counsel-projectile-find-file-matcher-basename (regexp candidates)
"Return the list of CANDIDATES whose basename matches REGEXP,
or if there is none the list of all CANDIDATES matching REGEXP.
Also uses `counsel--find-file-matcher' to ignore candidates based
on `counsel-find-file-ignore-regexp'."
(let ((cands (ivy--re-filter regexp candidates)))
(or (and (not (string= ivy-text ""))
;; We first filter `cands' to retain only matches in file
;; basename. This is almost copied from `ivy--re-filter'
;; because we can't quite use it directly.
(let ((re-list (if (stringp regexp)
(list (cons regexp t))
regexp))
(res cands))
(dolist (re re-list)
(setq res
(ignore-errors
(funcall
(if (cdr re)
#'cl-remove-if-not
#'cl-remove-if)
(let ((re-str (car re)))
(lambda (x)
(string-match re-str
(file-name-nondirectory x))))
res))))
;; We then apply `counsel--find-file-matcher' to `res'
;; so we can honor `ivy-use-ignore', but we don't need
;; to filter again.
(counsel--find-file-matcher nil res)))
;; We apply `counsel--find-file-matcher' to `cands' so we can
;; honor `ivy-use-ignore', but we don't need to filter
;; again.
(counsel--find-file-matcher nil cands))))
(defun counsel-projectile-find-file-action (file) (defun counsel-projectile-find-file-action (file)
"Find FILE and run `projectile-find-file-hook'." "Find FILE and run `projectile-find-file-hook'."
(find-file (projectile-expand-root file)) (find-file (projectile-expand-root file))
@ -303,7 +367,7 @@ With a prefix ARG, invalidate the cache first."
(projectile-maybe-invalidate-cache arg) (projectile-maybe-invalidate-cache arg)
(ivy-read (projectile-prepend-project-name "Find file: ") (ivy-read (projectile-prepend-project-name "Find file: ")
(projectile-current-project-files) (projectile-current-project-files)
:matcher #'counsel--find-file-matcher :matcher counsel-projectile-find-file-matcher
:require-match t :require-match t
:sort t :sort t
:action counsel-projectile-find-file-action :action counsel-projectile-find-file-action
@ -1071,10 +1135,11 @@ action."
(defun counsel-projectile--matcher (regexp _candidates) (defun counsel-projectile--matcher (regexp _candidates)
"Return REGEXP-matching CANDIDATES for `counsel-projectile'. "Return REGEXP-matching CANDIDATES for `counsel-projectile'.
Relies on `ivy--switch-buffer-matcher' and Relies on `ivy--switch-buffer-matcher' for buffers and the
`counsel--find-file-matcher'." matcher specified in `counsel-projectile-find-file-matcher' for
files."
(append (ivy--switch-buffer-matcher regexp counsel-projectile--buffers) (append (ivy--switch-buffer-matcher regexp counsel-projectile--buffers)
(counsel--find-file-matcher regexp counsel-projectile--non-visited-files))) (funcall counsel-projectile-find-file-matcher regexp counsel-projectile--non-visited-files)))
(defun counsel-projectile-action (name) (defun counsel-projectile-action (name)
"Switch to buffer or find file named NAME." "Switch to buffer or find file named NAME."