Emacs configuration and packages
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

12 KiB

Completion configuration using ivy, counsel and friends

selectrum

  (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)))

ivy

  (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)

counsel

  (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)

counsel add-ons

notmuch

  (use-package counsel-notmuch
    :ensure t
    :config (with-eval-after-load "gnus-group"
              (define-key gnus-group-mode-map "Gg" 'counsel-notmuch)))

recoll

  (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)

ivy rich

  (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)))))

cmap

  (jao-load-path "cmap")
  (use-package cmap
    :demand t
    :bind (("C-;" . cmap-cmap)
           ("C-'" . cmap-default)))

prompter

  (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)

minibuffer actions

  (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)

url / video actions

  (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)

hooks

  (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))

startup

  (ivy-mode 1)
  (counsel-mode 1)
  (ivy-rich-mode 1)
  (ivy-rich-project-root-cache-mode 1)