#+title: Completion configuration using ivy, counsel and friends * selectrum #+begin_src emacs-lisp :load no (use-package selectrum :ensure t :init (defun jao-selectrum--ord-refine (&rest args) (let ((completion-styles '(orderless))) (apply #'selectrum-refine-candidates-using-completions-styles args))) (defun jao-selectrum-orderless () (interactive) (setq selectrum-refine-candidates-function #'jao-selectrum--ord-refine) (setq selectrum-highlight-candidates-function #'orderless-highlight-matches) (setq orderless-skip-highlighting (lambda () selectrum-is-active))) :config ;; https://github.com/raxod502/selectrum/wiki/Ido,-icomplete(fido)-emulation (defun selectrum-fido-backward-updir () "Delete char before or go up directory, like `ido-mode'." (interactive) (if (and (eq (char-before) ?/) (eq (selectrum--get-meta 'category) 'file)) (save-excursion (goto-char (1- (point))) (when (search-backward "/" (point-min) t) (delete-region (1+ (point)) (point-max)))) (call-interactively 'backward-delete-char))) (defun selectrum-fido-delete-char () "Delete char or maybe call `dired', like `ido-mode'." (interactive) (let ((end (point-max))) (if (or (< (point) end) (not (eq (selectrum--get-meta 'category) 'file))) (call-interactively 'delete-char) (dired (file-name-directory (minibuffer-contents))) (exit-minibuffer)))) (defun selectrum-fido-ret () "Exit minibuffer or enter directory, like `ido-mode'." (interactive) (let* ((dir (and (eq (selectrum--get-meta 'category) 'file) (file-name-directory (minibuffer-contents)))) (current (selectrum-get-current-candidate)) (probe (and dir current (expand-file-name (directory-file-name current) dir)))) (if (and probe (file-directory-p probe) (not (string= current "./"))) (selectrum-insert-current-candidate) (selectrum-select-current-candidate)))) ;; (define-key selectrum-minibuffer-map (kbd "RET") 'selectrum-fido-ret) (define-key selectrum-minibuffer-map (kbd "DEL") 'selectrum-fido-backward-updir) (define-key selectrum-minibuffer-map (kbd "C-d") 'selectrum-fido-delete-char) :custom ((selectrum-complete-in-buffer t) ;; (selectrum-display-action '(display-buffer-at-bottom)) (selectrum-extend-current-candidate-highlight t) (selectrum-fix-vertical-window-height nil) (selectrum-max-window-height 20) (selectrum-show-indices nil) (selectrum-count-style 'current/matches)) :bind (("C-R" . selectrum-repeat))) #+end_src * ivy #+begin_src emacs-lisp (use-package ivy :ensure t :demand t :custom ((ivy-count-format "(%d/%d) ") (ivy-do-completion-in-region t) (ivy-height 20) (ivy-re-builders-alist '((counsel-ag . ivy--regex) (counsel-rg . ivy--regex) (counsel-yank-pop . ivy--regex) (swiper . ivy--regex) (swiper-isearch . ivy--regex) (t . ivy--regex-fuzzy))) (ivy-use-virtual-buffers t) (ivy-virtual-abbreviate 'abbreviate) (ivy-wrap t)) :config ;; used by ivy--regex-fuzzy to order results (use-package flx :ensure t) ;; Try C-o in the minibuffer (use-package ivy-hydra :after ivy :ensure t :init (setq ivy-read-action-function #'ivy-hydra-read-action)) (add-to-list 'ivy-initial-inputs-alist '(gnus-summary-move-article . "")) :bind (("C-R" . ivy-resume) ("C-x b" . ivy-switch-buffer) ("C-c v" . ivy-push-view) ("C-c V" . ivy-pop-view)) :diminish) #+end_src * counsel #+begin_src emacs-lisp (use-package counsel :ensure t :custom ((counsel-describe-function-function 'helpful-callable) (counsel-describe-variable-function 'helpful-variable) (counsel-find-file-at-point t) (counsel-linux-app-format-function #'counsel-linux-app-format-function-name-pretty) (counsel-mode-override-describe-bindings nil) (counsel-recentf-include-xdg-list t)) :config :bind (("C-s" . swiper-isearch) ("C-S-s" . isearch-forward) ("M-x" . counsel-M-x) ("C-x f" . counsel-find-file) ("C-c k" . counsel-ag) ("C-c K" . counsel-rg) ("C-c l" . counsel-locate) ("C-c b" . counsel-git) ("C-c i" . counsel-imenu) ("C-c G" . counsel-search) ("s-r" . counsel-linux-app)) :diminish) #+end_src * counsel add-ons *** notmuch #+begin_src emacs-lisp (use-package counsel-notmuch :ensure t :config (with-eval-after-load "gnus-group" (define-key gnus-group-mode-map "Gg" 'counsel-notmuch))) #+end_src *** recoll #+begin_src emacs-lisp (require 'jao-recoll) (defvar jao-counsel-recoll--history nil) (defun jao-counsel-recoll--function (str) (let ((xs (counsel-recoll-function str))) (cl-remove-if-not (lambda (x) (string-prefix-p "file://" x)) xs))) (defun jao-counsel-recoll (&optional initial-input) (interactive) (counsel-require-program "recoll") (ivy-read "recoll: " 'jao-counsel-recoll--function :initial-input initial-input :dynamic-collection t :history 'jao-counsel-recoll--history :action (lambda (x) (when (string-match "file://\\(.*\\)\\'" x) (let ((file-name (match-string 1 x))) (if (string-match "pdf$" x) (jao-open-doc file-name) (find-file file-name))))) :unwind #'counsel-delete-process :caller 'jao-counsel-recoll)) (defun jao-counsel-recoll--recoll (_s) (jao-recoll ivy-text)) (ivy-set-actions 'jao-counsel-recoll '(("x" jao-counsel-recoll--recoll "List in buffer"))) (global-set-key (kbd "C-c R") #'jao-counsel-recoll) #+end_src * ivy rich #+begin_src emacs-lisp (use-package ivy-rich :after (ivy counsel) :ensure t :custom ((ivy-rich-path-style 'relative) (ivy-rich-parse-remote-buffer nil) (ivy-rich-parse-remote-file-path nil)) :config (ivy-rich-modify-columns 'ivy-switch-buffer '((ivy-rich-candidate (:width 80)) (ivy-rich-switch-buffer-indicators (:face jao-themes-f00)) (ivy-rich-switch-buffer-project (:width 15)) (ivy-rich-switch-buffer-major-mode (:width 15 :face jao-themes-f12))))) #+end_src * cmap #+begin_src emacs-lisp (jao-load-path "cmap") (use-package cmap :demand t :bind (("C-;" . cmap-cmap) ("C-'" . cmap-default))) #+end_src *** prompter #+begin_src emacs-lisp (defun jao-cmap--hide-help () (when-let ((w (get-buffer-window (help-buffer)))) (with-selected-window w (kill-buffer-and-window)))) (defun jao-cmap--prompter (keymap) (let ((display-buffer-alist '(("*Help*" (display-buffer-at-bottom) (window-parameters (mode-line-format . none)) (window-height . fit-window-to-buffer))))) (let ((inhibit-message t)) (describe-keymap keymap)))) (defun jao-cmap--prompter-done () (save-current-buffer (jao-cmap--hide-help))) (setq cmap-prompter #'jao-cmap--prompter) (setq cmap-prompter-done #'jao-cmap--prompter-done) #+end_src *** minibuffer actions #+begin_src emacs-lisp (defun jao-cmap--completion-metadata () (completion-metadata (buffer-substring-no-properties (field-beginning) (point)) minibuffer-completion-table minibuffer-completion-predicate)) (defun jao-cmap--completion-category () (completion-metadata-get (jao-cmap--completion-metadata) 'category)) (defmacro cmap-define-keymap (v d &rest b) `(defvar ,v (cmap-keymap ,@b) ,d)) (cmap-define-keymap jao-cmap-buffer-map "Keymap for buffer actions." ("k" . kill-buffer) ("b" . switch-to-buffer) ("o" . switch-to-buffer-other-window) ("z" . bury-buffer) ("q" . kill-buffer-and-window) ("=" . ediff-buffers)) ;; (cmap-define-keymap espotify-item-keymap ;; "Actions for Spotify search results" ;; ("a" espotify--play-album) ;; ("h" espotify--show-info)) (defvar jao-cmap--smaps '((command . cmap-command-map) ;; (espotify-search-item . espotify-item-keymap) (function . cmap-function-map) (variable . cmap-variable-map) (face . cmap-face-map) (buffer . jao-cmap-buffer-map) (consult-buffer . jao-cmap-buffer-map))) (defun jao-cmap-target-minibuffer-candidate () (when (minibuffer-window-active-p (selected-window)) (let ((cand (ivy-state-current ivy-last)) (cat (jao-cmap--completion-category))) (when-let (m (alist-get cat jao-cmap--smaps)) (cons m cand))))) (add-to-list 'cmap-targets #'jao-cmap-target-minibuffer-candidate) #+end_src *** url / video actions #+begin_src emacs-lisp (defvar jao-cmap-video-url-rx (format "^https?://\\(?:www\\.\\)?%s/.+" (regexp-opt '("youtu.be" "youtube.com" "blip.tv" "vimeo.com" "infoq.com") t)) "A regular expression matching URLs that point to video streams") (defun jao-cmap--play-video (player url) (interactive "sURL: ") (let ((cmd (format "%s %s" player (shell-quote-argument url)))) (start-process-shell-command player nil cmd))) (defun jao-cmap-mpv (&optional url) "Play video stream with mpv" (interactive "sURL: ") (jao-cmap--play-video "mpv" url)) (defun jao-cmap-vlc (&optional url) "Play video stream with vlc" (interactive "sURL: ") (jao-cmap--play-video "vlc" url)) (defun jao-cmap-target-w3m-url () (when-let (url (or (thing-at-point-url-at-point) (w3m-anchor) w3m-current-url)) (cons 'cmap-url-map url))) (defun jao-cmap-kill (&optional x) "Save to kill ring" (interactive "s") (kill-new x)) (defun jao-cmap-url (url) "Browse URL, externally if we're already in emacs-w3m" (if (derived-mode-p 'w3m-mode) (jao-browse-with-external-browser url) (browse-url url))) (define-key cmap-url-map [return] #'jao-cmap-url) (define-key cmap-url-map "f" #'browse-url-firefox) (define-key cmap-url-map "w" #'jao-cmap-kill) (defun jao-cmap-target-video-url () (when-let (url (jao-cmap-target-w3m-url)) (when (string-match-p jao-cmap-video-url-rx (cdr url)) (cons 'jao-cmap-video-url-map (cdr url))))) (cmap-define-keymap jao-cmap-video-url-map "Actions on URLs pointing to remote video streams." ("v" . jao-cmap-vlc) ([return] . jao-cmap-mpv)) (add-to-list 'cmap-targets #'jao-cmap-target-w3m-url) (add-to-list 'cmap-targets #'jao-cmap-target-video-url) #+end_src * hooks #+begin_src emacs-lisp (with-eval-after-load "exwm" (add-to-list 'exwm-input-global-keys '([?\s-r] . counsel-linux-app))) (with-eval-after-load "espotify" (require 'ivy-spotify) (defalias 'jao-spotify-album #'ivy-spotify-album) (defalias 'jao-spotify-track #'ivy-spotify-track) (defalias 'jao-spotify-artist #'ivy-spotify-artist) (defalias 'jao-spotify-playlist #'ivy-spotify-playlist)) #+end_src * startup #+begin_src emacs-lisp (ivy-mode 1) (counsel-mode 1) (ivy-rich-mode 1) (ivy-rich-project-root-cache-mode 1) #+end_src