From b647c0c0679ca4f1c759ce0d5fb34e122ae3c364 Mon Sep 17 00:00:00 2001 From: fpi Date: Sat, 14 May 2022 23:53:07 +0200 Subject: Switch to vertico, consult, embark, marginalia --- emacs-init.org | 510 ++++++++++++++++++++------------------------------------- 1 file changed, 178 insertions(+), 332 deletions(-) diff --git a/emacs-init.org b/emacs-init.org index af95022..82f5a2b 100644 --- a/emacs-init.org +++ b/emacs-init.org @@ -1,5 +1,5 @@ # -*- coding: utf-8-unix -*- -#+PROPERTY: header-args:emacs-lisp :tangle tangle/emacs-10-init.el :results silent :noweb yes +#+PROPERTY: header-args:emacs-lisp :tangle tangle/emacs-10-init.el :results silent :noweb yes :tangle-mode (identity #o444) * Contents :QUOTE:TOC_2_gh: #+BEGIN_QUOTE - [[#overview][Overview]] @@ -28,6 +28,7 @@ - [[#tramp][Tramp]] - [[#git][Git]] - [[#projects][Projects]] + - [[#consult][Consult]] - [[#working-with-buffers][Working with buffers]] - [[#window-configuration][Window configuration]] - [[#file-encryption][File encryption]] @@ -55,6 +56,7 @@ - [[#context-aware-hydra][Context aware hydra]] - [[#ssh-tunnels][SSH tunnels]] - [[#minor-utilities][Minor utilities]] + - [[#embark][Embark]] - [[#language-settings][Language settings]] - [[#spellcheck][Spellcheck]] - [[#interface][Interface]] @@ -1904,343 +1906,79 @@ any recursive editing levels." * Selection and search methods ** Completion frameworks Having used ido, ivy, icicles and helm in the past, I'm trying to -settle for something simple and go back to ido. The settings below -are for now mostly copied from [[https://gitlab.com/protesilaos/dotemacs/][Protesilaos Stavrou]]. -*** Minibuffer settings -#+begin_src emacs-lisp -(use-package minibuffer - :config - - ;; Super-powerful completion style for out-of-order groups of matches - ;; using a comprehensive set of matching styles. - (use-package orderless - :straight t - :config - (setq orderless-regexp-separator "[/\s_-]+") - (setq orderless-matching-styles - '(orderless-flex - orderless-strict-leading-initialism - orderless-regexp - orderless-prefixes - orderless-literal)) - - (defun prot/orderless-literal-dispatcher (pattern _index _total) - (when (string-suffix-p "=" pattern) - `(orderless-literal . ,(substring pattern 0 -1)))) - - (defun prot/orderless-initialism-dispatcher (pattern _index _total) - (when (string-suffix-p "," pattern) - `(orderless-strict-leading-initialism . ,(substring pattern 0 -1)))) - - (setq orderless-style-dispatchers '(prot/orderless-literal-dispatcher - prot/orderless-initialism-dispatcher)) - :bind (:map minibuffer-local-completion-map - ("SPC" . nil) ; space should never complete - ("?" . nil))) ; valid regexp character - - (setq completion-styles - '(orderless partial-completion)) - (setq completion-category-defaults nil) - (setq completion-cycle-threshold 3) - (setq completion-flex-nospace nil) - (setq completion-pcm-complete-word-inserts-delimiters t) - (setq completion-pcm-word-delimiters "-_./:| ") - (setq completion-show-help t) - (setq completion-ignore-case t) - (setq read-buffer-completion-ignore-case t) - (setq read-file-name-completion-ignore-case t) - (setq completions-format 'vertical) ; *Completions* buffer - (setq enable-recursive-minibuffers t) - (setq read-answer-short t) - (setq resize-mini-windows t) - - (file-name-shadow-mode 1) - (minibuffer-depth-indicate-mode 1) - (minibuffer-electric-default-mode 1) - - (defun prot/focus-minibuffer () - "Focus the active minibuffer. - -Bind this to `completion-list-mode-map' to M-v to easily jump -between the list of candidates present in the \\*Completions\\* -buffer and the minibuffer (because by default M-v switches to the -completions if invoked from inside the minibuffer." - (interactive) - (let ((mini (active-minibuffer-window))) - (when mini - (select-window mini)))) - - (defun prot/focus-minibuffer-or-completions () - "Focus the active minibuffer or the \\*Completions\\*. - -If both the minibuffer and the Completions are present, this -command will first move per invocation to the former, then the -latter, and then continue to switch between the two. +settle for something simple. -The continuous switch is essentially the same as running -`prot/focus-minibuffer' and `switch-to-completions' in -succession." - (interactive) - (let* ((mini (active-minibuffer-window)) - ;; This could be hardened a bit, but I am okay with it. - (completions (or (get-buffer-window "*Completions*") - (get-buffer-window "*Embark Live Occur*")))) - (cond ((and mini - (not (minibufferp))) - (select-window mini nil)) - ((and completions - (not (eq (selected-window) - completions))) - (select-window completions nil))))) - - ;; Technically, this is not specific to the minibuffer, but I define - ;; it here so that you can see how it is also used from inside the - ;; "Completions" buffer - (defun prot/describe-symbol-at-point (&optional arg) - "Get help (documentation) for the symbol at point. - -With a prefix argument, switch to the *Help* window. If that is -already focused, switch to the most recently used window -instead." - (interactive "P") - (let ((symbol (symbol-at-point))) - (when symbol - (describe-symbol symbol))) - (when arg - (let ((help (get-buffer-window "*Help*"))) - (when help - (if (not (eq (selected-window) help)) - (select-window help) - (select-window (get-mru-window))))))) - - ;; This will be deprecated in favour of the `embark' package - (defun prot/completions-kill-save-symbol () - "Add symbol-at-point to the kill ring. - -Intended for use in the \\*Completions\\* buffer. Bind this to a -key in `completion-list-mode-map'." - (interactive) - (kill-new (thing-at-point 'symbol))) - - -;;;; DEPRECATED in favour of the `embark' package (see further below), -;;;; which implements the same functionality in a more efficient way. -;; (defun prot/complete-kill-or-insert-candidate (&optional arg) -;; "Place the matching candidate to the top of the `kill-ring'. -;; This will keep the minibuffer session active. -;; -;; With \\[universal-argument] insert the candidate in the most -;; recently used buffer, while keeping focus on the minibuffer. -;; -;; With \\[universal-argument] \\[universal-argument] insert the -;; candidate and immediately exit all recursive editing levels and -;; active minibuffers. -;; -;; Bind this function in `icomplete-minibuffer-map'." -;; (interactive "*P") -;; (let ((candidate (car completion-all-sorted-completions))) -;; (when (and (minibufferp) -;; (or (bound-and-true-p icomplete-mode) -;; (bound-and-true-p live-completions-mode))) ; see next section -;; (cond ((eq arg nil) -;; (kill-new candidate)) -;; ((= (prefix-numeric-value arg) 4) -;; (with-minibuffer-selected-window (insert candidate))) -;; ((= (prefix-numeric-value arg) 16) -;; (with-minibuffer-selected-window (insert candidate)) -;; (top-level)))))) - - ;; Defines, among others, aliases for common actions to Super-KEY. - ;; Normally these should go in individual package declarations, but - ;; their grouping here makes things easier to understand. - :bind (("s-f" . find-file) - ("s-F" . find-file-other-window) - ("s-d" . dired) - ("s-D" . dired-other-window) - ("s-b" . switch-to-buffer) - ("s-B" . switch-to-buffer-other-window) - ("s-h" . prot/describe-symbol-at-point) - ("s-H" . (lambda () - (interactive) - (prot/describe-symbol-at-point '(4)))) - ("s-v" . prot/focus-minibuffer-or-completions) - :map minibuffer-local-completion-map - ("" . minibuffer-force-complete-and-exit) - ("C-j" . exit-minibuffer) - ;;;; DEPRECATED in favour of the `embark' package - ;; ("M-o w" . prot/complete-kill-or-insert-candidate) - ;; ("M-o i" . (lambda () - ;; (interactive) - ;; (prot/complete-kill-or-insert-candidate '(4)))) - ;; ("M-o j" . (lambda () - ;; (interactive) - ;; (prot/complete-kill-or-insert-candidate '(16)))) - :map completion-list-mode-map - ("h" . prot/describe-symbol-at-point) - ("w" . prot/completions-kill-save-symbol) - ("n" . next-line) - ("p" . previous-line) - ("f" . next-completion) - ("b" . previous-completion) - ("M-v" . prot/focus-minibuffer))) -#+end_src -*** Icomplete -#+begin_src emacs-lisp -(use-package icomplete - :demand - :after minibuffer ; Read that section as well - :config - (setq icomplete-delay-completions-threshold 100) - (setq icomplete-max-delay-chars 2) - (setq icomplete-compute-delay 0.2) - (setq icomplete-show-matches-on-no-input t) - (setq icomplete-hide-common-prefix nil) - (setq icomplete-prospects-height 1) - ;; (setq icomplete-separator " · ") - ;; (setq icomplete-separator " │ ") - ;; (setq icomplete-separator " ┆ ") - ;; (setq icomplete-separator " ¦ ") - (setq icomplete-separator (propertize " ┆ " 'face 'shadow)) - (setq icomplete-with-completion-tables t) - (setq icomplete-in-buffer t) - (setq icomplete-tidy-shadowed-file-names nil) - - (fido-mode -1) ; Emacs 27.1 - (icomplete-mode 1) - - (defun prot/icomplete-minibuffer-truncate () - "Truncate minibuffer lines in `icomplete-mode'. - This should only affect the horizontal layout and is meant to - enforce `icomplete-prospects-height' being set to 1. - - Hook it to `icomplete-minibuffer-setup-hook'." - (when (and (minibufferp) - (bound-and-true-p icomplete-mode)) - (setq truncate-lines t))) - - ;; Note that the the syntax for `use-package' hooks is controlled by - ;; the `use-package-hook-name-suffix' variable. The "-hook" suffix is - ;; not an error of mine. - :hook (icomplete-minibuffer-setup-hook . prot/icomplete-minibuffer-truncate) - :bind (:map icomplete-minibuffer-map - ("" . icomplete-force-complete) - ("" . icomplete-force-complete-and-exit) ; exit with completion - ("C-j" . exit-minibuffer) ; force input unconditionally - ("C-n" . icomplete-forward-completions) - ("" . icomplete-forward-completions) - ("" . icomplete-forward-completions) - ("C-p" . icomplete-backward-completions) - ("" . icomplete-backward-completions) - ("" . icomplete-backward-completions) - ;; The following command is from Emacs 27.1 - ("" . icomplete-fido-backward-updir))) -#+end_src -*** Icomplete-vertical -#+begin_src emacs-lisp -(use-package icomplete-vertical +For =vertico= use =M-RET= to end repeated minibuffer reads instead of =RET=. +#+begin_src emacs-lisp +;; Vertico & Marginalia +(use-package vertico + :straight t + :init (vertico-mode 1)) +(use-package vertico-posframe :straight t - :demand - :after (minibuffer icomplete) ; do not forget to check those as well :config - (setq icomplete-vertical-prospects-height (/ (frame-height) 6)) - (icomplete-vertical-mode -1) - - (defun prot/kill-ring-yank-complete () - "Insert the selected `kill-ring' item directly at point. -When region is active, `delete-region'. - -Sorting of the `kill-ring' is disabled. Items appear as they -normally would when calling `yank' followed by `yank-pop'." + (defun fpi/vertico-posframe-toggle () (interactive) - (let ((kills ; do not sort items - (lambda (string pred action) - (if (eq action 'metadata) - '(metadata (display-sort-function . identity) - (cycle-sort-function . identity)) - (complete-with-action - action kill-ring string pred))))) - (icomplete-vertical-do - (:separator 'dotted-line :height (/ (frame-height) 4)) - (when (use-region-p) - (delete-region (region-beginning) (region-end))) - (insert - (completing-read "Yank from kill ring: " kills nil t))))) - - :bind (("s-y" . prot/kill-ring-yank-complete) - :map icomplete-minibuffer-map - ("C-v" . icomplete-vertical-toggle))) -#+end_src -*** Ido -:PROPERTIES: -:header-args:emacs-lisp: :tangle no -:END: -#+BEGIN_SRC emacs-lisp -(use-package ido - :init - (setq ido-everywhere t) - (setq ido-enable-flex-matching t) - (setq ido-enable-regexp nil) - (setq ido-enable-prefix nil) - (setq ido-all-frames nil) - (setq ido-buffer-disable-smart-matches t) - (setq ido-completion-buffer "*Ido Completions*") - (setq ido-completion-buffer-all-completions nil) - (setq ido-confirm-unique-completion nil) - (setq ido-create-new-buffer 'prompt) - (setq ido-default-buffer-method 'selected-window) - (setq ido-default-file-method 'selected-window) - (setq ido-enable-last-directory-history t) - (setq ido-use-filename-at-point nil) - (setq ido-use-url-at-point nil) - (setq ido-use-virtual-buffers t) - (setq ido-use-faces t) - (setq ido-max-window-height 1) - (setq ido-decorations - '(" " - " " - " | " - " | …" - "[" - "]" - " [No match]" - " [Matched]" - " [Not readable]" - " [Too big]" - " [Confirm]" - " " - " ")) - (setq ido-auto-merge-work-directories-length -1) - :config - (ido-mode 1) - :hook - (minibuffer-setup . (lambda () - (visual-line-mode 1) - (setq-local truncate-lines nil) - (setq-local resize-mini-windows nil) - (setq-local max-mini-window-height 1)))) -#+END_SRC - -#+BEGIN_SRC emacs-lisp :tangle no -(use-package ido-completing-read+ + (if vertico-posframe-mode + (progn + (vertico-posframe-cleanup) + (vertico-posframe-mode -1)) + (vertico-posframe-mode 1))) + :bind (:map vertico-map + ("C-," . fpi/vertico-posframe-toggle)) + :init (vertico-posframe-mode 1)) +(use-package marginalia :straight t - :after ido - :config - (ido-ubiquitous-mode 1)) -#+END_SRC -*** amx -Ido completion for =M-x=. -#+BEGIN_SRC emacs-lisp :tangle no -(use-package amx + :bind (:map minibuffer-local-map + ("M-A" . marginalia-cycle)) + :init (marginalia-mode 1)) + +;; Orderless & minibuffer settings +(use-package orderless :straight t - :after (ido ido-completing-read+) - :init - (setq amx-backend 'ido) - (setq amx-save-file "~/.emacs.d/amx-items") - (setq amx-history-length 10) - (setq amx-show-key-bindings nil) :config - (amx-mode 1)) -#+END_SRC + (setq orderless-matching-styles + '(orderless-regexp + orderless-initialism)) + (defmacro fpi/orderless-dispatcher (name suffix style) + "Define a orderless dispatcher function NAME using key SUFFIX to +call STYLE." + (backquote + (defun ,name (pattern _index _total) + (when (string-suffix-p ,suffix pattern) + (backquote (,style ,backquote-unquote-symbol(substring pattern 0 -1))))))) + (fpi/orderless-dispatcher + fpi/orderless-literal-dispatcher "=" orderless-literal) + (fpi/orderless-dispatcher + fpi/orderless-initialism-dispatcher "," orderless-initialism) + (fpi/orderless-dispatcher + fpi/orderless-flex-dispatcher "~" orderless-flex) + (setq orderless-style-dispatchers + '(fpi/orderless-literal-dispatcher + fpi/orderless-initialism-dispatcher + fpi/orderless-flex-dispatcher))) +(use-package minibuffer + :after (consult orderless) + :custom + ;; Make tramp host completion work. See vertico documentation. + (completion-styles '(orderless basic)) + (completion-category-overrides '((file (styles basic partial-completion)))) + + ;; Make completion-at-point use vertico + (completion-in-region-function + (lambda (&rest args) + (apply (if vertico-mode + #'consult-completion-in-region + #'completion--in-region) + args)))) + +;; General settings +(setq enable-recursive-minibuffers t) +(setq read-answer-short t) +(file-name-shadow-mode 1) +(minibuffer-depth-indicate-mode 1) +(minibuffer-electric-default-mode 1) +#+end_src ** isearch enhancements Once again this is mostly taken from [[https://gitlab.com/protesilaos/dotemacs/][Protesilaos Stavrou]]. @@ -2957,6 +2695,88 @@ Projectile should be fully replaceable with =project.el=. Though some packages m ;; (projectile-mode 1) :bind (("C-c p" . projectile-command-map))) #+END_SRC +** Consult +A bundle of common functions. Mostly drop in replacements for ~find-file~, ~grep~, ~find~, etc. +#+begin_src emacs-lisp +(define-prefix-command 'fpi/consult-map nil "consult-map") +(use-package consult + :straight t + :bind + ;; C-x bindings (ctl-x-map) + (("C-x b" . consult-buffer) ;; orig. switch-to-buffer + ("C-x 4 b" . consult-buffer-other-window) ;; orig. switch-to-buffer-other-window + ("C-x 5 b" . consult-buffer-other-frame) ;; orig. switch-to-buffer-other-frame + ("C-x r b" . consult-bookmark) ;; orig. bookmark-jump + ("C-x p b" . consult-project-buffer) ;; orig. project-switch-to-buffer + ) + ;; M-g, M-s + ((" a" . consult-apropos) ;; orig. apropos-command + ;; M-g bindings (goto-map) + ("M-g e" . consult-compile-error) + ("M-g f" . consult-flymake) ;; Alternative: consult-flycheck + ("M-g g" . consult-goto-line) ;; orig. goto-line + ("M-g M-g" . consult-goto-line) ;; orig. goto-line + ("M-g o" . consult-outline) ;; Alternative: consult-org-heading + ("M-g m" . consult-mark) + ("M-g k" . consult-global-mark) + ;; M-s bindings (search-map) + ("M-s d" . consult-find) + ("M-s D" . consult-locate) + ("M-s g" . consult-grep) + ("M-s G" . consult-git-grep) + ("M-s r" . consult-ripgrep) + ("M-s l" . consult-line) + ("M-s L" . consult-line-multi) + ("M-s m" . consult-multi-occur) + ("M-s u" . consult-focus-lines) + ;; Isearch integration + ("M-s e" . consult-isearch-history) + :map isearch-mode-map + ("M-s e" . consult-isearch-history) ;; orig. isearch-edit-string + ("M-s l" . consult-line) ;; needed by consult-line to detect isearch + ("M-s L" . consult-line-multi) ;; needed by consult-line to detect isearch + ;; Minibuffer history + :map minibuffer-local-map + ("M-s" . consult-history) ;; orig. next-matching-history-element + ("M-r" . consult-history)) ;; orig. previous-matching-history-element + (:map fpi/consult-map + ;; C-c bindings (mode-specific-map) + ("h" . consult-history) + ("m" . consult-mode-command) + ) + :config + ;; Optionally configure preview. The default value + ;; is 'any, such that any key triggers the preview. + ;; (setq consult-preview-key 'any) + ;; (setq consult-preview-key (kbd "M-.")) + ) +(use-package consult-flyspell + :after consult + :bind (("M-s s" . consult-flyspell))) +(use-package consult-org-roam + :straight t + :after (consult org-roam) + :custom + (consult-org-roam-grep-func #'consult-ripgrep) + :config + ;; Eventually suppress previewing for certain functions + ;; (consult-customize + ;; consult-org-roam-forward-links + ;; :preview-key (kbd "M-.")) + :bind + ("C-c n e" . consult-org-roam-file-find) + ("C-c n b" . consult-org-roam-backlinks) + ("C-c n r" . consult-org-roam-search)) +#+end_src + +Fix pdf-tools =goto-page= command: +#+begin_src emacs-lisp :noweb-ref pdf-tools-config :tangle no +(define-key pdf-view-mode-map [remap consult-goto-line] 'pdf-view-goto-page) +#+end_src + +#+begin_src emacs-lisp :noweb-ref fpi-bindings :tangle no +(fpi/define-key fpi-map (kbd ".") #'fpi/consult-map "Consult") +#+end_src ** Working with buffers This renames buffers with the same name and uniqifies them using angled @@ -3167,7 +2987,9 @@ make sure to compile the tex document with the option ~--synctex=1~. :straight t :config (setq pdf-info-epdfinfo-program (concat user-emacs-directory "epdfinfo")) - (pdf-tools-install)) + (pdf-tools-install) + <> + ) #+END_SRC Add support for pdf annotations. Rebind ~pdf-annot-minor-mode-map~ to @@ -6467,6 +6289,27 @@ The =Auto-Insert= package helps inserting header templates upon creating files. "#!/usr/bin/env bash" \n \n)) (auto-insert-mode -1)) #+end_src +** Embark +Do things in the order =selection → action= instead of =action → selection=. +#+begin_src emacs-lisp +(use-package embark + :straight t + :bind (("C-." . embark-act))) +(use-package embark-consult + :straight t + :after (embark consult)) +#+end_src + +As minor-mode maps would override the global map we have to unset the =C-.= binding in flyspell. We can either do this with an ~eval-after-load~ +#+begin_src emacs-lisp :tangle no :eval never +(eval-after-load "flyspell" + '(define-key flyspell-mode-map (kbd "C-.") nil)) +#+end_src + +or by (un-)setting the binding explicitly in the =use-package= call of flyspell. +#+begin_src emacs-lisp :tangle no :noweb-ref flyspell-bindings +("C-." . nil) +#+end_src * Language settings End sentences with single spaces. #+begin_src emacs-lisp @@ -6487,6 +6330,9 @@ Setup mainly from [[https://github.com/howardabrams/dot-files/blob/master/emacs. #+begin_src emacs-lisp (use-package flyspell :delight + :bind (:map flyspell-mode-map + <> + ) :init (add-hook 'prog-mode-hook 'flyspell-prog-mode) (dolist (hook '(text-mode-hook org-mode-hook)) -- cgit v1.2.3