doomemacs/modules/completion/ivy/autoload/ivy.el
Henrik Lissner a66872fe25
Focus on ripgrep; remove ag, git-grep & grep support
We're focusing on ripgrep so we can iterate on search functionality in
Doom quicker. There is nothing the other search backends can do that
ripgrep can't. It is now a hard dependency for Doom.
2019-11-17 01:19:59 -05:00

365 lines
14 KiB
EmacsLisp

;;; completion/ivy/autoload/ivy.el -*- lexical-binding: t; -*-
(defun +ivy--is-workspace-buffer-p (buffer)
(let ((buffer (car buffer)))
(when (stringp buffer)
(setq buffer (get-buffer buffer)))
(+workspace-contains-buffer-p buffer)))
(defun +ivy--is-workspace-other-buffer-p (buffer)
(let ((buffer (car buffer)))
(when (stringp buffer)
(setq buffer (get-buffer buffer)))
(and (not (eq buffer (current-buffer)))
(+workspace-contains-buffer-p buffer))))
;;;###autoload
(defun +ivy-rich-buffer-name (candidate)
"Display the buffer name.
Buffers that are considered unreal (see `doom-real-buffer-p') are dimmed with
`+ivy-buffer-unreal-face'."
(let ((b (get-buffer candidate)))
(when (null uniquify-buffer-name-style)
(when-let* ((file-path (buffer-file-name b))
(uniquify-buffer-name-style 'forward))
(setq candidate
(uniquify-get-proposed-name
(replace-regexp-in-string "<[0-9]+>$" "" (buffer-name b))
(directory-file-name
(if file-path
(file-name-directory file-path)
default-directory))
1))))
(cond ((ignore-errors
(file-remote-p
(buffer-local-value 'default-directory b)))
(ivy-append-face candidate 'ivy-remote))
((doom-unreal-buffer-p b)
(ivy-append-face candidate +ivy-buffer-unreal-face))
((not (buffer-file-name b))
(ivy-append-face candidate 'ivy-subdir))
((buffer-modified-p b)
(ivy-append-face candidate 'ivy-modified-buffer))
(candidate))))
;;;###autoload
(defun +ivy-rich-buffer-icon (candidate)
"Display the icon for CANDIDATE buffer."
;; NOTE This is inspired by `all-the-icons-ivy-buffer-transformer', minus the
;; buffer name and extra padding as those are handled by `ivy-rich'.
(propertize "\t" 'display
(if-let* ((buffer (get-buffer candidate))
(mode (buffer-local-value 'major-mode buffer)))
(or
(all-the-icons-ivy--icon-for-mode mode)
(all-the-icons-ivy--icon-for-mode (get mode 'derived-mode-parent))
(funcall
all-the-icons-ivy-family-fallback-for-buffer
all-the-icons-ivy-name-fallback-for-buffer))
(all-the-icons-icon-for-file candidate))))
;;;###autoload
(defun +ivy-rich-describe-variable-transformer (cand)
"Previews the value of the variable in the minibuffer"
(let* ((sym (intern cand))
(val (and (boundp sym) (symbol-value sym)))
(print-level 3))
(replace-regexp-in-string
"[\n\t\^[\^M\^@\^G]" " "
(cond ((booleanp val)
(propertize (format "%s" val) 'face
(if (null val)
'font-lock-comment-face
'success)))
((symbolp val)
(propertize (format "'%s" val)
'face 'highlight-quoted-symbol))
((keymapp val)
(propertize "<keymap>" 'face 'font-lock-constant-face))
((listp val)
(prin1-to-string val))
((stringp val)
(propertize (format "%S" val) 'face 'font-lock-string-face))
((numberp val)
(propertize (format "%s" val) 'face 'highlight-numbers-number))
((format "%s" val)))
t)))
;;
;; Library
(defun +ivy--switch-buffer-preview ()
(let (ivy-use-virtual-buffers ivy--virtual-buffers)
(counsel--switch-buffer-update-fn)))
(defalias '+ivy--switch-buffer-preview-all #'counsel--switch-buffer-update-fn)
(defalias '+ivy--switch-buffer-unwind #'counsel--switch-buffer-unwind)
(defun +ivy--switch-buffer (workspace other)
(let ((current (not other))
prompt action filter update unwind)
(cond ((and workspace current)
(setq prompt "Switch to workspace buffer: "
action #'ivy--switch-buffer-action
filter #'+ivy--is-workspace-other-buffer-p))
(workspace
(setq prompt "Switch to workspace buffer in other window: "
action #'ivy--switch-buffer-other-window-action
filter #'+ivy--is-workspace-buffer-p))
(current
(setq prompt "Switch to buffer: "
action #'ivy--switch-buffer-action))
((setq prompt "Switch to buffer in other window: "
action #'ivy--switch-buffer-other-window-action)))
(when +ivy-buffer-preview
(cond ((not (and ivy-use-virtual-buffers
(eq +ivy-buffer-preview 'everything)))
(setq update #'+ivy--switch-buffer-preview
unwind #'+ivy--switch-buffer-unwind))
((setq update #'+ivy--switch-buffer-preview-all
unwind #'+ivy--switch-buffer-unwind))))
(ivy-read prompt 'internal-complete-buffer
:action action
:predicate filter
:update-fn update
:unwind unwind
:preselect (buffer-name (other-buffer (current-buffer)))
:matcher #'ivy--switch-buffer-matcher
:keymap ivy-switch-buffer-map
;; NOTE A clever disguise, needed for virtual buffers.
:caller #'ivy-switch-buffer)))
;;;###autoload
(defun +ivy/switch-workspace-buffer (&optional arg)
"Switch to another buffer within the current workspace.
If ARG (universal argument), open selection in other-window."
(interactive "P")
(+ivy--switch-buffer t arg))
;;;###autoload
(defun +ivy/switch-workspace-buffer-other-window ()
"Switch another window to a buffer within the current workspace."
(interactive)
(+ivy--switch-buffer t t))
;;;###autoload
(defun +ivy/switch-buffer ()
"Switch to another buffer."
(interactive)
(+ivy--switch-buffer nil nil))
;;;###autoload
(defun +ivy/switch-buffer-other-window ()
"Switch to another buffer in another window."
(interactive)
(+ivy--switch-buffer nil t))
;;;###autoload
(defun +ivy/woccur ()
"Invoke a wgrep buffer on the current ivy results, if supported."
(interactive)
(unless (window-minibuffer-p)
(user-error "No completion session is active"))
(require 'wgrep)
(let ((caller (ivy-state-caller ivy-last)))
(if-let* ((occur-fn (plist-get +ivy-edit-functions caller)))
(ivy-exit-with-action
(lambda (_) (funcall occur-fn)))
(if-let* ((occur-fn (plist-get ivy--occurs-list caller)))
(let ((buffer (generate-new-buffer
(format "*ivy-occur%s \"%s\"*"
(if caller (concat " " (prin1-to-string caller)) "")
ivy-text))))
(with-current-buffer buffer
(let ((inhibit-read-only t))
(erase-buffer)
(funcall occur-fn))
(setf (ivy-state-text ivy-last) ivy-text)
(setq ivy-occur-last ivy-last)
(setq-local ivy--directory ivy--directory))
(ivy-exit-with-action
`(lambda (_)
(pop-to-buffer ,buffer)
(ivy-wgrep-change-to-wgrep-mode))))
(user-error "%S doesn't support wgrep" caller)))))
;;;###autoload
(defun +ivy-yas-prompt (prompt choices &optional display-fn)
(yas-completing-prompt prompt choices display-fn #'ivy-completing-read))
;;;###autoload
(defun +ivy-git-grep-other-window-action (x)
"Opens the current candidate in another window."
(when (string-match "\\`\\(.*?\\):\\([0-9]+\\):\\(.*\\)\\'" x)
(select-window
(with-ivy-window
(let ((file-name (match-string-no-properties 1 x))
(line-number (match-string-no-properties 2 x)))
(find-file-other-window (expand-file-name file-name (ivy-state-directory ivy-last)))
(goto-char (point-min))
(forward-line (1- (string-to-number line-number)))
(re-search-forward (ivy--regex ivy-text t) (line-end-position) t)
(run-hooks 'counsel-grep-post-action-hook)
(selected-window))))))
;;;###autoload
(defun +ivy-confirm-delete-file (x)
(dired-delete-file x 'confirm-each-subdirectory))
;;
;;; File searching
;;;###autoload
(defun +ivy/projectile-find-file ()
"A more sensible `counsel-projectile-find-file', which will revert to
`counsel-find-file' if invoked from $HOME, `counsel-file-jump' if invoked from a
non-project, `projectile-find-file' if in a big project (more than
`ivy-sort-max-size' files), or `counsel-projectile-find-file' otherwise.
The point of this is to avoid Emacs locking up indexing massive file trees."
(interactive)
(call-interactively
(cond ((or (file-equal-p default-directory "~")
(when-let (proot (doom-project-root))
(file-equal-p proot "~")))
#'counsel-find-file)
((doom-project-p)
(let ((files (projectile-current-project-files)))
(if (<= (length files) ivy-sort-max-size)
#'counsel-projectile-find-file
#'projectile-find-file)))
(#'counsel-file-jump))))
;;;###autoload
(cl-defun +ivy-file-search (&key query in all-files (recursive t))
"Conduct a file search using ripgrep.
:query STRING
Determines the initial input to search for.
:in PATH
Sets what directory to base the search out of. Defaults to the current
project's root.
:recursive BOOL
Whether or not to search files recursively from the base directory."
(declare (indent defun))
(unless (executable-find "rg")
(user-error "Couldn't find ripgrep in your PATH"))
(require 'counsel)
(let* ((ivy-more-chars-alist '((t . 1)))
(project-root (or (doom-project-root) default-directory))
(directory (or in project-root))
(default-directory directory)
(args (concat (if all-files " -uu")
(unless recursive " --maxdepth 1"))))
(counsel-rg
(or (if query query)
(when (use-region-p)
(let ((beg (or (bound-and-true-p evil-visual-beginning) (region-beginning)))
(end (or (bound-and-true-p evil-visual-end) (region-end))))
(when (> (abs (- end beg)) 1)
(let ((query (buffer-substring-no-properties beg end)))
;; Escape characters that are special to ivy searches
(replace-regexp-in-string "[! |]" (lambda (substr)
(cond ((and (string= substr " ")
(not (featurep! +fuzzy)))
" ")
((string= substr "|")
"\\\\\\\\|")
((concat "\\\\" substr))))
(rxt-quote-pcre query)))))))
directory args
(format "rg%s %s"
args
(cond ((equal directory default-directory)
"./")
((equal directory project-root)
(projectile-project-name))
((file-relative-name directory project-root)))))))
;;;###autoload
(defun +ivy/project-search (&optional arg initial-query directory)
"Performs a live project search from the project root using ripgrep.
If ARG (universal argument), include all files, even hidden or compressed ones,
in the search."
(interactive "P")
(+ivy-file-search :query initial-query :in directory :all-files arg))
;;;###autoload
(defun +ivy/project-search-from-cwd (&optional arg initial-query)
"Performs a project search recursively from the current directory.
If ARG (universal argument), include all files, even hidden or compressed ones."
(interactive "P")
(+ivy/project-search arg initial-query default-directory))
;;
;;; Wrappers around `counsel-compile'
;;;###autoload
(defun +ivy/compile ()
"Execute a compile command from the current buffer's directory."
(interactive)
(counsel-compile default-directory))
;;;###autoload
(defun +ivy/project-compile ()
"Execute a compile command from the current project's root."
(interactive)
(counsel-compile (projectile-project-root)))
;;;###autoload
(defun +ivy/jump-list ()
"Go to an entry in evil's (or better-jumper's) jumplist."
(interactive)
;; REVIEW Refactor me
(let (buffers)
(unwind-protect
(ivy-read "jumplist: "
(nreverse
(delete-dups
(delq
nil
(mapcar (lambda (mark)
(when mark
(cl-destructuring-bind (path pt _id) mark
(let ((buf (get-file-buffer path)))
(unless buf
(push (setq buf (find-file-noselect path t))
buffers))
(with-current-buffer buf
(goto-char pt)
(font-lock-fontify-region (line-beginning-position) (line-end-position))
(cons (format "%s:%d: %s"
(buffer-name)
(line-number-at-pos)
(string-trim-right (or (thing-at-point 'line) "")))
(point-marker)))))))
(cddr (better-jumper-jump-list-struct-ring
(better-jumper-get-jumps (better-jumper--get-current-context))))))))
:sort nil
:require-match t
:action (lambda (cand)
(let ((mark (cdr cand)))
(delq! (marker-buffer mark) buffers)
(mapc #'kill-buffer buffers)
(setq buffers nil)
(with-current-buffer (switch-to-buffer (marker-buffer mark))
(goto-char (marker-position mark)))))
:caller '+ivy/jump-list)
(mapc #'kill-buffer buffers))))
;;;###autoload
(defun +ivy/git-grep-other-window-action ()
"Open the current counsel-{ag,rg,git-grep} candidate in other-window."
(interactive)
(ivy-set-action #'+ivy-git-grep-other-window-action)
(setq ivy-exit 'done)
(exit-minibuffer))