;;; 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) (setq candidate (replace-regexp-in-string "<[0-9]+>$" "" candidate))) (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-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 "" '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))) ;;;###autoload (defun +ivy-format-function-line-or-arrow (cands) "Transform CANDS into a string for minibuffer. If in terminal, prefix candidates with a chevron to make it more obvious which one you're selecting, especially in themes that can't set a good background for `ivy-current-match'. This is a combination of `ivy-format-function-line' and `ivy-format-function-arrow'. In the GUI, this is the same as `ivy-format-function-line'." (if (display-graphic-p) (ivy-format-function-line cands) (ivy--format-function-generic (lambda (str) (ivy--add-face (concat "> " str "\n") 'ivy-current-match)) (lambda (str) (concat " " str "\n")) cands ""))) ;; ;; 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-fn (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 or /, `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) ;; Spoof the command so that ivy/counsel will display the (well fleshed-out) ;; actions list for `counsel-find-file' on C-o. The actions list for the other ;; commands aren't as well configured or are empty. (let ((this-command 'counsel-find-file)) (call-interactively (cond ((or (file-equal-p default-directory "~") (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) prompt args) "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* ((this-command 'counsel-rg) (project-root (or (doom-project-root) default-directory)) (directory (or in project-root)) (args (concat (if all-files " -uu") (unless recursive " --maxdepth 1") " --hidden -g!.git " (mapconcat #'shell-quote-argument args " ")))) (setq deactivate-mark t) (counsel-rg (or query (when (doom-region-active-p) (replace-regexp-in-string "[! |]" (lambda (substr) (cond ((and (string= substr " ") (not (modulep! +fuzzy))) " ") ((string= substr "|") "\\\\\\\\|") ((concat "\\\\" substr)))) (rxt-quote-pcre (doom-thing-at-point-or-region))))) directory args (or prompt (format "Search project [%s]: " (cond ((equal directory default-directory) "./") ((equal directory project-root) (projectile-project-name)) ((file-relative-name directory project-root))) (string-trim args)))))) ;;;###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) ;; Fix unhelpful 'Couldn't find project root' error (letf! (defun counsel--compile-root () (ignore-errors (funcall counsel--compile-root))) (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))