;;; ycp-complete.el -- My config for minibuffer and completions -*- lexical-binding: t -*- ;; Copyright (C) 2023 Free Software Foundation. ;; Author: Yuchen Pei ;; Package-Requires: ((emacs "28.2")) ;; This file is part of dotfiles. ;; dotfiles is free software: you can redistribute it and/or modify it under ;; the terms of the GNU Affero General Public License as published by ;; the Free Software Foundation, either version 3 of the License, or ;; (at your option) any later version. ;; dotfiles is distributed in the hope that it will be useful, but WITHOUT ;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY ;; or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General ;; Public License for more details. ;; You should have received a copy of the GNU Affero General Public ;; License along with dotfiles. If not, see . ;;; Commentary: ;; My config for minibuffer and completions. ;;; Code: ;; completion, minibuffer, corfu, tempel ;; part adapted from prot-dotfiles (my-package minibuffer (setq completion-styles '(basic partial-completion emacs22 orderless)) (setq completion-category-overrides ;; NOTE 2021-10-25: I am adding `basic' because it works better as a ;; default for some contexts. Read: ;; . ;; ;; `partial-completion' is a killer app for files, because it ;; can expand ~/.l/s/fo to ~/.local/share/fonts. ;; ;; If `basic' cannot match my current input, Emacs tries the ;; next completion style in the given order. In other words, ;; `orderless' kicks in as soon as I input a space or one of its ;; style dispatcher characters. '((file (styles . (basic partial-completion orderless))) (project-file (styles . (basic substring partial-completion orderless))) (imenu (styles . (emacs22 substring orderless))) (kill-ring (styles . (emacs22 substring orderless))) (consult-location (styles . (emacs22 substring orderless))) (eglot (styles . (emacs22 substring orderless))))) (setq completion-ignore-case t) (setq read-buffer-completion-ignore-case t) (setq read-file-name-completion-ignore-case t) ;; disable space to run minibuffer-complete-word (my-keybind minibuffer-mode-map "SPC" nil "?" nil) (my-keybind minibuffer-local-completion-map "SPC" nil "?" nil) (setq enable-recursive-minibuffers t) (minibuffer-depth-indicate-mode 1) (setq resize-mini-windows t) (setq read-answer-short t) (setq echo-keystrokes 0.25) ;; Do not allow the cursor to move inside the minibuffer prompt. I ;; got this from the documentation of Daniel Mendler's Vertico ;; package: . (setq minibuffer-prompt-properties '(read-only t cursor-intangible t face minibuffer-prompt)) (add-hook 'minibuffer-setup-hook #'cursor-intangible-mode) (minibuffer-electric-default-mode 1)) ;;;; `savehist' (minibuffer and related histories) (my-package savehist (setq savehist-file (locate-user-emacs-file "savehist")) (setq history-length 500) (setq history-delete-duplicates t) (setq savehist-save-minibuffer-history t) (setq savehist-additional-variables '(register-alist kill-ring)) (savehist-mode 1)) ;;;; `dabbrev' (dynamic word completion (dynamic abbreviations)) (my-package dabbrev (setq dabbrev-abbrev-char-regexp "\\sw\\|\\s_") (setq dabbrev-abbrev-skip-leading-regexp "[$*/=~']") (setq dabbrev-backward-only nil) (setq dabbrev-case-distinction 'case-replace) (setq dabbrev-case-fold-search nil) (setq dabbrev-case-replace 'case-replace) (setq dabbrev-check-other-buffers t) (setq dabbrev-eliminate-newlines t) (setq dabbrev-upcase-means-case-search t)) ;;; icomplete (my-package icomplete (icomplete-vertical-mode t) (setq icomplete-show-matches-on-no-input t) (setq icomplete-prospects-height 4) (setq icomplete-scroll t) (setq icomplete-matches-format "[%s/%s] ") (require 'my-complete) (my-keybind icomplete-minibuffer-map "" #'icomplete-force-complete "M-" #'minibuffer-complete "C-M-i" #'minibuffer-complete "C-s" #'icomplete-forward-completions "C-r" #'icomplete-backward-completions "C-v" #'my-icomplete-vertical-forward-page "M-v" #'my-icomplete-vertical-backward-page "" nil)) (my-package recentf (setq recentf-max-saved-items 1000) (setq recentf-exclude '("~\\'" "\\`out\\'" "\\.log\\'" "^/[^/]*:" "\\.el\\.gz\\'" "~$" "/mnt/" "^/tmp/")) (recentf-mode 1) ;; disable recentf-save-list on quit on non-emacs-client so that it does not ;; overwrite the recentf file (require 'my-utils) (unless (my-server-p) (setq kill-emacs-hook (delete 'recentf-save-list kill-emacs-hook))) (require 'my-complete) (my-server-timer recentf-timer nil 300 'my-recentf-save-list-silently) ) ;;; corfu (my-package corfu (:install t) (:delay 5) (global-corfu-mode 1) (corfu-popupinfo-mode 1) (setq corfu-auto t corfu-cycle t corfu-separator ?\s) (define-key corfu-map [remap next-line] nil) (define-key corfu-map [remap previous-line] nil) (define-key corfu-map [remap beginning-of-buffer] nil) (define-key corfu-map [remap end-of-buffer] nil) (define-key corfu-map [remap move-beginning-of-line] nil) (define-key corfu-map [remap move-end-of-line] nil) (my-keybind corfu-map "C-j" #'corfu-insert "" nil "C-s" #'corfu-next "C-r" #'corfu-previous "C-a" nil ) (require 'my-corfu) (add-hook 'minibuffer-setup-hook #'my-corfu-enable-always-in-minibuffer 1) ;;; corfu does not work well in gud as it "flushes" completion ;;; suggestions to the buffer ;;; https://github.com/minad/corfu/issues/157 ;; Only company modes works with bbdb email completion in ;; message-mode, so we remove corfu from message-mode to avoid ;; overlapping multiple completion dropdowns (setq global-corfu-modes '((not gud-mode) (not message-mode) t)) ) ;;; We still need company mode because corfu does not work well in ;;; certain modes yet (my-package company (:install t) (:delay 5) ;; corfu does not complete email fields using bbdb (add-hook 'message-mode-hook #'company-mode) ;; for some reason, having a t in the completion-at-point-functions ;; causes company to hang in message-mode (add-hook 'message-mode-hook (lambda () (setq-local completion-at-point-functions (delq t completion-at-point-functions)) )) (setq company-idle-delay .1 company-minimum-prefix-length 3 company-selection-wrap-around t company-show-numbers t company-require-match 'never) (my-keybind company-active-map "\t" #'company-complete-selection "" #'company-complete-selection "" nil "" nil "C-j" #'company-complete-selection "C-s" #'company-select-next "C-r" #'company-select-previous )) ;;; cape (my-package cape (:install t) (:delay 15) (setq cape-dabbrev-min-length 3) (setq cape-symbol-wrapper '((org-mode ?~ ?~) (markdown-mode ?` ?`) (log-edit-mode ?' ?') (message-mode ?' ?'))) (dolist (backend '(cape-elisp-symbol cape-keyword cape-file cape-history cape-dabbrev)) (add-to-list 'completion-at-point-functions backend)) ;; for some reason, cape-dabbrev causes message-mode to hang with ;; company mode as well ;; (add-hook 'message-mode-hook ;; (lambda () ;; (add-to-list 'completion-at-point-functions ;; 'cape-dabbrev))) ) (my-package imenu (:delay 5) (my-keybind global-map "M-g i" #'imenu) ) ;;; consult (my-package consult (:install t) (:delay 10) (setq consult-line-numbers-widen t) ;; (setq completion-in-region-function #'consult-completion-in-region) (setq consult-async-min-input 3) (setq consult-async-input-debounce 0.5) (setq consult-async-input-throttle 0.8) (setq consult-narrow-key ">") (setq register-preview-delay 0.8 register-preview-function #'consult-register-format) (setq consult-find-args "find . -not ( -path */.git* -prune )") (setq consult-preview-key 'any) (add-to-list 'consult-mode-histories '(vc-git-log-edit-mode . log-edit-comment-ring)) (add-hook 'completion-list-mode-hook #'consult-preview-at-point-mode) (require 'consult-imenu) ; the `imenu' extension is in its own file (require 'my-consult) (my-keybind global-map "C-x b" #'consult-buffer "C-z" #'consult-buffer "C-x l" #'consult-locate "M-K" #'consult-keep-lines ; M-S-k is similar to M-S-5 (M-%) "M-F" #'consult-focus-lines ; same principle "M-g g" #'consult-goto-line "M-s M-b" #'consult-buffer "M-s M-f" #'consult-find "M-s M-G" #'my-consult-grep "M-s M-g" #'my-consult-grep-default "M-s M-h" #'consult-history "M-s M-i" #'consult-imenu "M-s M-l" #'consult-line "M-s M-m" #'consult-mark "M-y" #'consult-yank-pop "M-s M-s" #'consult-outline) (my-keybind consult-narrow-map "?" #'consult-narrow-help) (my-keybind minibuffer-local-map "C-s" #'consult-history) ) ;;; marginalia (my-package marginalia (:install t) (:delay 10) (setq marginalia-max-relative-age 0) (my-keybind minibuffer-local-map "M-A" #'marginalia-cycle) (marginalia-mode 1)) (setq tempel-path (locate-user-emacs-file "*tempel-templates")) (my-package tempel (:install t) (:delay 15) (require 'my-tempel) (my-keybind global-map "M-=" #'tempel-complete ; Alternative: `tempel-expand' "M-*" #'tempel-insert) (my-keybind tempel-map "RET" #'tempel-done "C-p" #'tempel-previous "C-n" #'tempel-next "" #'tempel-next "" #'tempel-previous "C-S-" #'tempel-previous) (require 'my-tempel) (my-add-hooks #'my-tempel-setup-capf '(prog-mode-hook text-mode-hook)) (add-hook 'eglot-managed-mode-hook #'my-tempel-setup-capf) ) ;; consult-recoll (my-package consult-recoll (:delay 30) (:install t) ) (my-package hmm (:delay 60) (my-read-local-config) (my-setq-from-local hmm-web-search-engines hmm-transformers hmm-handlers) (require 'my-net) (setq hmm-web-browsers '((:name eww :command eww) (:name luwak :command luwak-open) (:name firefox :command browse-url-firefox) (:name firefox-private :command my-browse-url-firefox-private) (:name tor-browser :command my-browse-url-tor-browser) (:name mullvad-browser :command my-browse-url-mullvad) (:name qutebrowser :command my-browse-url-qutebrowser) (:name download-and-open :command my-fetch-url))) (setq hmm-external-handlers '((:name mpv :external-command "mpv %U" :display-name "mpv player" :description "Play url with mpv" :schemes ("ftp" "http" "https" "mms" "rtmp" "rtsp" "sftp" "smb" "srt") :handling :url) (:name wget :external-command "wget %U" :display-name "GNU Wget" :description "The non-interactive network downloader" :schemes ("ftp" "http" "https") :handling :url) (:name qutebrowser :external-command "qutebrowser %U" :display-name "qutebrowser" :description "A keyboard-driven, vim-like browser based on PyQt5" :schemes ("http" "https") :handling :url) (:name torsocks-mpv :external-command "torsocks mpv %U" :display-name "mpv player torsocks" :description "Play url with mpv over torsocks" :schemes ("ftp" "http" "https" "mms" "rtmp" "rtsp" "sftp" "smb" "srt") :handling :url) (:name clean-elc-compile :external-command "~/bin/clean-elc-compile %f" :description "Clean compile a directory of elisp files" :display-buffer t :mimetypes ("inode/directory") :handling :file) (:name mogrify-strip :external-command "mogrify -strip %F" :description "Strip images of all profiles and comments" :display-buffer t :handling :file) (:name pacfind :external-command "pacfind %f" :description "Find the pacman package containing a command" :display-buffer t :handling :query))) (setq hmm-matchers '(((thing-at-point-url-at-point) . hmm-url) ((thing-at-point-file-at-point) . hmm-file) ((and (derived-mode-p 'dired-mode) (dired-get-filename nil t)) . hmm-file) ((and (derived-mode-p 'dired-mode) (expand-file-name default-directory)) . hmm-file) ((and (derived-mode-p 'org-mode) (my-org-link-at-point)) . hmm-url) ((get-text-property (point) 'shr-url) . hmm-url) ((and (derived-mode-p 'luwak-mode) (get-text-property (point) 'url)) . hmm-url) ((and (derived-mode-p 'luwak-mode) (plist-get luwak-data :url)) . hmm-url) ((thing-at-point 'symbol) . hmm-query) ((buffer-file-name) . hmm-file) ((expand-file-name default-directory) . hmm-file))) (hmm-update)) (provide 'ycp-complete)