cp-switch-buffer, cp: Improve preview and make it optional

Avoid displaying the ` *counsel-projectile*` buffer when switching to
a project with no open buffer.

Also preview non-visited files if
`counsel-switch-buffer-preview-virtual-buffers` is non-nil.

Only trigger preview if `counsel-projectile-preview-buffers` is
non-nil (default is nil).

Update readme.

Closes #171
This commit is contained in:
Eric Danan 2020-10-15 12:09:48 +01:00
parent 293aa37800
commit 06b03c1080
2 changed files with 102 additions and 40 deletions

View file

@ -341,6 +341,8 @@ If one of these variable is nil, the default, the command's candidates are not s
``` ```
Note that the `counsel-projectile` command always sorts buffers before files. Buffers are sorted as in `counsel-projectile-switch-to-buffer` and files are sorted according to `counsel-projectile-find-file`. Note that the `counsel-projectile` command always sorts buffers before files. Buffers are sorted as in `counsel-projectile-switch-to-buffer` and files are sorted according to `counsel-projectile-find-file`.
## Preview in `counsel-projectile-switch-to-buffer` and `counsel-projectile`
If the variable `counsel-projectile-preview-buffers` is non-nil, `counsel-projectile-switch-to-buffer` and `counsel-projectile` display a preview of the selected buffer in the current window. This makes these commands behave similarly to `counsel-switch-buffer`. If `counsel-switch-buffer-preview-virtual-buffers` is also non-nil, `counsel-projectile` also displays a preview of the selected non-visited file.
# Upgrading # Upgrading
## Breaking changes in version `0.3` ## Breaking changes in version `0.3`
### Key bindings ### Key bindings

View file

@ -525,6 +525,17 @@ candidates list of `counsel-projectile-switch-to-buffer' and
:type 'boolean :type 'boolean
:group 'counsel-projectile) :group 'counsel-projectile)
(defcustom counsel-projectile-preview-buffers nil
"When non-nil, `counsel-projectile-switch-to-buffer' and `counsel-projectile'
display a preview of the selected buffer in the current window.
This makes these commands behave similarly to `counsel-switch-buffer'. If
`counsel-switch-buffer-preview-virtual-buffers' is also non-nil,
`counsel-projectile' also displays a preview of the selected
non-visited file."
:type 'boolean
:group 'counsel-projectile)
(counsel-projectile--defcustom-action (counsel-projectile--defcustom-action
'counsel-projectile-switch-to-buffer 'counsel-projectile-switch-to-buffer
'(1 '(1
@ -586,31 +597,70 @@ This simply applies the same transformer as in `ivy-switch-buffer', which is `iv
(funcall (ivy-alist-setting ivy--display-transformers-alist 'ivy-switch-buffer) (funcall (ivy-alist-setting ivy--display-transformers-alist 'ivy-switch-buffer)
str)) str))
(defun counsel-projectile--switch-to-buffer-update-fn ()
"Update function for `counsel-projectile--switch-to-buffer'."
;; Adapted from counsel--switch-buffer-update-fn.
(unless counsel--switch-buffer-previous-buffers
(setq counsel--switch-buffer-previous-buffers (buffer-list)))
(when counsel-projectile-preview-buffers
(when (member (ivy-state-current ivy-last) ivy-marked-candidates)
(setf (ivy-state-current ivy-last)
(substring (ivy-state-current ivy-last) (length ivy-mark-prefix))))
;; Unlike in `counsel-switch-buffer' we could have an empty string
;; as candidate if no project buffer is open.
(cond
((and (not (string= (ivy-state-current ivy-last) ""))
(get-buffer (ivy-state-current ivy-last)))
(let ((ivy-marked-candidates nil))
(ivy-call)))
;; Following case if for use in `counsel-projectile'.
((and (not (string= (ivy-state-current ivy-last) ""))
counsel-switch-buffer-preview-virtual-buffers
(file-exists-p (projectile-expand-root (ivy-state-current ivy-last))))
(let ((buf (ignore-errors
;; may not open due to `large-file-warning-threshold' etc.
(find-file-noselect (projectile-expand-root (ivy-state-current ivy-last))))))
(if buf
(progn
(push buf counsel--switch-buffer-temporary-buffers)
(ivy-call))
;; clean up the minibuffer so that there's no delay before
;; the Ivy candidates are displayed once again
(message ""))))
(t
(with-ivy-window
(switch-to-buffer (or (plist-get (ivy-state-extra-props ivy-last) :from-buffer)
(ivy-state-buffer ivy-last))))))))
;;;###autoload ;;;###autoload
(defun counsel-projectile-switch-to-buffer () (defun counsel-projectile-switch-to-buffer (&optional from-buffer)
"Jump to a buffer in the current project." "Jump to a buffer in the current project.
If `counsel-projectile-preview-buffers' is non-nil, display a
preview of the selected ivy completion candidate buffer as in
`counsel-switch-buffer', falling back to the current buffer or
optionally FROM-BUFFER."
(interactive) (interactive)
(if (and (eq projectile-require-project-root 'prompt) (if (and (eq projectile-require-project-root 'prompt)
(not (projectile-project-p))) (not (projectile-project-p)))
(counsel-projectile-switch-to-buffer-action-switch-project) (counsel-projectile-switch-to-buffer-action-switch-project)
(let ((ivy-update-fns-alist (ivy-read (projectile-prepend-project-name "Switch to buffer: ")
'((counsel-projectile-switch-to-buffer . counsel--switch-buffer-update-fn))) ;; We use a collection function so that it is called each
(ivy-unwind-fns-alist ;; time the `ivy-state' is reset. This is needed for the
'((counsel-projectile-switch-to-buffer . counsel--switch-buffer-unwind)))) ;; "kill buffer" action.
(ivy-read (projectile-prepend-project-name "Switch to buffer: ") #'counsel-projectile--project-buffers
;; We use a collection function so that it is called each :matcher #'ivy--switch-buffer-matcher
;; time the `ivy-state' is reset. This is needed for the :require-match t
;; "kill buffer" action. :sort counsel-projectile-sort-buffers
#'counsel-projectile--project-buffers :action counsel-projectile-switch-to-buffer-action
:matcher #'ivy--switch-buffer-matcher :keymap counsel-projectile-switch-to-buffer-map
:require-match t :extra-props (list :from-buffer from-buffer)
:sort counsel-projectile-sort-buffers :caller 'counsel-projectile-switch-to-buffer)))
:action counsel-projectile-switch-to-buffer-action
:keymap counsel-projectile-switch-to-buffer-map
:caller 'counsel-projectile-switch-to-buffer))))
(ivy-configure 'counsel-projectile-switch-to-buffer (ivy-configure 'counsel-projectile-switch-to-buffer
:display-transformer-fn #'counsel-projectile-switch-to-buffer-transformer) :display-transformer-fn #'counsel-projectile-switch-to-buffer-transformer
:update-fn #'counsel-projectile--switch-to-buffer-update-fn
:unwind-fn #'counsel--switch-buffer-unwind)
;;* counsel-projectile-grep ;;* counsel-projectile-grep
@ -1247,9 +1297,10 @@ action."
(defun counsel-projectile-switch-project-action (project) (defun counsel-projectile-switch-project-action (project)
"Jump to a file or buffer in PROJECT." "Jump to a file or buffer in PROJECT."
(let ((projectile-switch-project-action (let* ((from-buffer (ivy-state-buffer ivy-last))
(lambda () (projectile-switch-project-action
(counsel-projectile ivy-current-prefix-arg)))) `(lambda ()
(counsel-projectile ivy-current-prefix-arg ,from-buffer))))
(counsel-projectile-switch-project-by-name project))) (counsel-projectile-switch-project-by-name project)))
(defun counsel-projectile-switch-project-action-find-file (project) (defun counsel-projectile-switch-project-action-find-file (project)
@ -1280,7 +1331,10 @@ action."
(defun counsel-projectile-switch-project-action-switch-to-buffer (project) (defun counsel-projectile-switch-project-action-switch-to-buffer (project)
"Jump to a buffer in PROJECT." "Jump to a buffer in PROJECT."
(let ((projectile-switch-project-action 'counsel-projectile-switch-to-buffer)) (let* ((from-buffer (ivy-state-buffer ivy-last))
(projectile-switch-project-action
`(lambda ()
(counsel-projectile-switch-to-buffer ,from-buffer))))
(counsel-projectile-switch-project-by-name project))) (counsel-projectile-switch-project-by-name project)))
(defun counsel-projectile-switch-project-action-save-all-buffers (project) (defun counsel-projectile-switch-project-action-save-all-buffers (project)
@ -1553,34 +1607,40 @@ directory of file named NAME."
(propertize str 'face 'ivy-virtual))) (propertize str 'face 'ivy-virtual)))
;;;###autoload ;;;###autoload
(defun counsel-projectile (&optional arg) (defun counsel-projectile (&optional arg from-buffer)
"Jump to a buffer or file in the current project. "Jump to a buffer or file in the current project.
With a prefix ARG, invalidate the cache first. With a prefix ARG, invalidate the cache first.
If not inside a project, call `counsel-projectile-switch-project'." If `counsel-projectile-preview-buffers' is non-nil, display a
preview of the selected ivy completion candidate buffer as in
`counsel-switch-buffer', falling back to the current buffer or
optionally FROM-BUFFER.
If `counsel-switch-buffer-preview-virtual-buffers' is also
non-nil, also display a preview of the selected ivy completion
candidate non-visited file."
(interactive "P") (interactive "P")
(if (and (eq projectile-require-project-root 'prompt) (if (and (eq projectile-require-project-root 'prompt)
(not (projectile-project-p))) (not (projectile-project-p)))
(counsel-projectile-action-switch-project) (counsel-projectile-action-switch-project)
(let ((ivy-update-fns-alist (projectile-maybe-invalidate-cache arg)
'((counsel-projectile . counsel--switch-buffer-update-fn))) (ivy-read (projectile-prepend-project-name "Load buffer or file: ")
(ivy-unwind-fns-alist ;; We use a collection function so that it is called each
'((counsel-projectile . counsel--switch-buffer-unwind)))) ;; time the `ivy-state' is reset. This is needed for the
(projectile-maybe-invalidate-cache arg) ;; "kill buffer" action.
(ivy-read (projectile-prepend-project-name "Load buffer or file: ") #'counsel-projectile--project-buffers-and-files
;; We use a collection function so that it is called each :matcher #'counsel-projectile--matcher
;; time the `ivy-state' is reset. This is needed for the :require-match t
;; "kill buffer" action. :action counsel-projectile-action
#'counsel-projectile--project-buffers-and-files :keymap counsel-projectile-map
:matcher #'counsel-projectile--matcher :extra-props (list :from-buffer from-buffer)
:require-match t :caller 'counsel-projectile)))
:action counsel-projectile-action
:keymap counsel-projectile-map
:caller 'counsel-projectile))))
(ivy-configure 'counsel-projectile (ivy-configure 'counsel-projectile
:display-transformer-fn #'counsel-projectile-transformer) :display-transformer-fn #'counsel-projectile-transformer
:update-fn #'counsel-projectile--switch-to-buffer-update-fn
:unwind-fn #'counsel--switch-buffer-unwind)
;;* counsel-projectile-mode ;;* counsel-projectile-mode