Files
.emacs.d/lisp/init-completion.el

302 lines
12 KiB
EmacsLisp
Raw Normal View History

2025-12-14 04:51:24 +11:00
;;; init-completion.el --- Initialize completion configurations. -*- lexical-binding: t -*-
;; Copyright (C) 2016-2025 Vincent Zhang
;; Author: Vincent Zhang <seagle0128@gmail.com>
;; URL: https://github.com/seagle0128/.emacs.d
;; This file is not part of GNU Emacs.
;;
;; This program is free software; you can redistribute it and/or
;; modify it under the terms of the GNU General Public License as
;; published by the Free Software Foundation; either version 3, or
;; (at your option) any later version.
;;
;; This program 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
;; General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 51 Franklin Street, Fifth
;; Floor, Boston, MA 02110-1301, USA.
;;
;;; Commentary:
;;
;; Modern completion configuration.
;;
;; Optionally use the `orderless' completion style.
(use-package orderless
:custom
2026-04-03 19:00:38 +11:00
(completion-styles '(orderless partial-completion basic))
2025-12-14 04:51:24 +11:00
(completion-category-defaults nil)
2025-12-15 00:26:46 +11:00
(completion-category-overrides '((file (styles orderless partial-completion))))
2025-12-14 04:51:24 +11:00
(orderless-component-separator #'orderless-escapable-split-on-space))
;; Support Pinyin
(use-package pinyinlib
:after orderless
:functions orderless-regexp
:autoload pinyinlib-build-regexp-string
:init
(defun orderless-regexp-pinyin (str)
"Match COMPONENT as a pinyin regex."
(orderless-regexp (pinyinlib-build-regexp-string str)))
(add-to-list 'orderless-matching-styles 'orderless-regexp-pinyin))
;; VERTical Interactive COmpletion
(use-package vertico
:custom (vertico-count 15)
:bind (:map vertico-map
2026-03-24 22:21:29 +11:00
("RET" . vertico-directory-enter)
("DEL" . vertico-directory-delete-char)
("M-DEL" . vertico-directory-delete-word))
2026-04-02 19:21:30 +11:00
:hook (elpaca-after-init . vertico-mode)
2025-12-15 18:20:19 +11:00
:hook (rfn-eshadow-update-overlay . vertico-directory-tidy))
2025-12-14 04:51:24 +11:00
2025-12-15 00:34:32 +11:00
;; (use-package vertico-posframe
;; :after vertico
;; :hook (vertico-mode . vertico-posframe-mode))
2025-12-15 00:26:46 +11:00
(use-package vertico-multiform
2026-04-02 19:21:30 +11:00
:ensure nil
2025-12-15 00:26:46 +11:00
:hook (vertico-mode . vertico-multiform-mode)
:config
(defvar +vertico-transform-functions nil)
(cl-defmethod vertico--format-candidate :around
(cand prefix suffix index start &context ((not +vertico-transform-functions) null))
(dolist (fun (ensure-list +vertico-transform-functions))
(setq cand (funcall fun cand)))
(cl-call-next-method cand prefix suffix index start))
(defun +vertico-highlight-directory (file)
"If FILE ends with a slash, highlight it as a directory."
(when (string-suffix-p "/" file)
(add-face-text-property 0 (length file) 'marginalia-file-priv-dir 'append file))
file)
(defun +vertico-highlight-enabled-mode (cmd)
"If MODE is enabled, highlight it as font-lock-constant-face."
(let ((sym (intern cmd)))
(with-current-buffer (nth 1 (buffer-list))
(if (or (eq sym major-mode)
(and
(memq sym minor-mode-list)
(boundp sym)
(symbol-value sym)))
(add-face-text-property 0 (length cmd) 'font-lock-constant-face 'append cmd)))
cmd))
(add-to-list 'vertico-multiform-categories
'(file
(+vertico-transform-functions . +vertico-highlight-directory)))
(add-to-list 'vertico-multiform-commands
'(execute-extended-command
(+vertico-transform-functions . +vertico-highlight-enabled-mode))))
2025-12-14 04:51:24 +11:00
;; Enrich existing commands with completion annotations
(use-package marginalia
2026-04-02 19:21:30 +11:00
:hook (elpaca-after-init . marginalia-mode))
2025-12-15 18:20:19 +11:00
2025-12-14 04:51:24 +11:00
2025-12-15 00:26:46 +11:00
;; Add icons to completion candidates
2025-12-15 00:34:32 +11:00
(use-package nerd-icons-completion
2025-12-15 18:20:19 +11:00
:hook (marginalia-mode . nerd-icons-completion-marginalia-setup))
2025-12-15 00:26:46 +11:00
2025-12-14 04:51:24 +11:00
;; Consulting completing-read
(use-package consult
2026-03-29 19:12:45 +11:00
:commands consult-customize
2025-12-15 01:04:39 +11:00
:bind (("C-." . consult-imenu)
2026-03-24 22:21:29 +11:00
("C-c T" . consult-theme)
([remap Info-search] . consult-info)
2025-12-22 18:39:07 +11:00
;; ([remap isearch-forward] . consult-line)
([remap recentf-open-files] . consult-recent-file)
2026-04-02 15:31:43 +11:00
([remap bookmark-jump] . consult-bookmark)
2025-12-22 18:39:07 +11:00
("C-x M-:" . consult-complex-command) ;; orig. repeat-complex-command
;; Custom M-# bindings for fast register access
("M-#" . consult-register-load)
("M-'" . consult-register-store) ;; orig. abbrev-prefix-mark (unrelated)
("C-M-#" . consult-register)
;; Other custom bindings
("M-y" . consult-yank-pop) ;; orig. yank-pop
;; M-g bindings in `goto-map'
("M-g e" . consult-compile-error)
("M-g r" . consult-grep-match)
2026-01-15 01:07:26 +11:00
("M-g f" . consult-flycheck) ;; Alternative: consult-flycheck
2025-12-22 18:39:07 +11:00
("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-g i" . consult-imenu)
("M-g I" . consult-imenu-multi)
2026-03-24 22:21:29 +11:00
2026-01-15 01:07:26 +11:00
;; M-g bindings in `search-map'
2026-03-24 22:21:29 +11:00
("s-f" . consult-line)
2026-01-15 01:07:26 +11:00
("M-g d" . consult-find) ;; Alternative: consult-fd
("M-g c" . consult-locate)
("M-g G" . consult-git-grep)
("M-g r" . consult-ripgrep)
("M-g l" . consult-line)
("M-g L" . consult-line-multi)
("M-g k" . consult-keep-lines)
("M-g u" . consult-focus-lines)
2026-03-24 22:21:29 +11:00
("C-x C-r" . consult-recent-file)
("C-x b" . consult-buffer)
2025-12-22 18:39:07 +11:00
2026-01-15 01:07:26 +11:00
("M-g e" . consult-isearch-history)
2025-12-22 18:39:07 +11:00
:map isearch-mode-map
("M-e" . consult-isearch-history) ;; orig. isearch-edit-string
2026-01-15 01:07:26 +11:00
("M-g e" . consult-isearch-history) ;; orig. isearch-edit-string
("M-g l" . consult-line) ;; needed by consult-line to detect isearch
("M-g L" . consult-line-multi))
2025-12-15 18:20:19 +11:00
:hook (completion-list-mode . consult-preview-at-point-mode)
:init
(setq register-preview-delay 0.5
2025-12-22 18:39:07 +11:00
register-preview-function #'consult-register-format)
(setq xref-show-xrefs-function #'consult-xref
xref-show-definitions-function #'consult-xref))
2025-12-21 14:53:44 +11:00
2025-12-22 19:52:11 +11:00
;; (use-package consult-projectile)
(use-package consult-flycheck)
2025-12-14 04:51:24 +11:00
(use-package consult-dir
:bind (("C-x C-d" . consult-dir)
:map minibuffer-local-completion-map
("C-x C-d" . consult-dir)
("C-x C-j" . consult-dir-jump-file)))
2025-12-15 00:26:46 +11:00
(use-package consult-yasnippet
:bind ("M-g y" . consult-yasnippet))
2025-12-14 04:51:24 +11:00
(use-package embark
:commands embark-prefix-help-command
:bind (("C-;" . embark-act)
("M-." . embark-dwim) ; overrides `xref-find-definitions'
([remap describe-bindings] . embark-bindings)
:map minibuffer-local-map
("M-." . my-embark-preview))
:init
;; Optionally replace the key help with a completing-read interface
2026-02-21 03:41:32 +11:00
(setq prefix-help-command #'embark-prefix-help-command)
:config
(eval-when-compile
(defmacro my/embark-ace-action (fn)
`(defun ,(intern (concat "my/embark-ace-" (symbol-name fn))) ()
(interactive)
(with-demoted-errors "%s"
(require 'ace-window)
(let ((aw-dispatch-always t))
(aw-switch-to-window (aw-select nil))
(call-interactively (symbol-function ',fn)))))))
(define-key embark-file-map (kbd "o") (my/embark-ace-action find-file))
(define-key embark-buffer-map (kbd "o") (my/embark-ace-action switch-to-buffer))
(define-key embark-bookmark-map (kbd "o") (my/embark-ace-action bookmark-jump))
(eval-when-compile
(defmacro my/embark-split-action (fn split-type)
`(defun ,(intern (concat "my/embark-"
(symbol-name fn)
"-"
(car (last (split-string
(symbol-name split-type) "-"))))) ()
(interactive)
(funcall #',split-type)
(call-interactively #',fn))))
(define-key embark-file-map (kbd "2") (my/embark-split-action find-file split-window-below))
(define-key embark-buffer-map (kbd "2") (my/embark-split-action switch-to-buffer split-window-below))
(define-key embark-bookmark-map (kbd "2") (my/embark-split-action bookmark-jump split-window-below))
(define-key embark-file-map (kbd "3") (my/embark-split-action find-file split-window-right))
(define-key embark-buffer-map (kbd "3") (my/embark-split-action switch-to-buffer split-window-right))
(define-key embark-bookmark-map (kbd "3") (my/embark-split-action bookmark-jump split-window-right)))
2025-12-14 04:51:24 +11:00
(use-package embark-consult
:bind (:map minibuffer-mode-map
2026-03-24 22:21:29 +11:00
("C-c C-o" . embark-export))
2025-12-14 04:51:24 +11:00
:hook (embark-collect-mode . consult-preview-at-point-mode))
;; Auto completion
(use-package corfu
:custom
(corfu-auto t)
(corfu-auto-prefix 2)
(corfu-count 12)
(corfu-preview-current nil)
(corfu-on-exact-match nil)
(corfu-auto-delay 0.2)
(corfu-popupinfo-delay '(0.4 . 0.2))
(global-corfu-modes '((not erc-mode
circe-mode
help-mode
gud-mode
vterm-mode)
t))
:custom-face
(corfu-border ((t (:inherit region :background unspecified))))
:bind ("M-/" . completion-at-point)
:init
(global-corfu-mode)
(corfu-history-mode)
(corfu-popupinfo-mode)
:config
;;Quit completion before saving
(add-hook 'before-save-hook #'corfu-quit)
2025-12-15 18:20:19 +11:00
(advice-add #'persistent-scratch-save :before #'corfu-quit)
2026-04-03 19:00:38 +11:00
(add-to-list 'corfu-continue-commands #'corfu-move-to-minibuffer))
2025-12-14 04:51:24 +11:00
2026-04-03 19:00:38 +11:00
(use-package nerd-icons-corfu
:init
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
2025-12-15 00:26:46 +11:00
2025-12-14 04:51:24 +11:00
;; A few more useful configurations...
(use-package emacs
2026-04-02 19:21:30 +11:00
:ensure nil
2025-12-14 04:51:24 +11:00
:custom
;; TAB cycle if there are only few candidates
;; (completion-cycle-threshold 3)
;; Enable indentation+completion using the TAB key.
;; `completion-at-point' is often bound to M-TAB.
(tab-always-indent 'complete)
;; Emacs 30 and newer: Disable Ispell completion function. As an alternative,
;; try `cape-dict'.
(text-mode-ispell-word-completion nil)
;; Emacs 28 and newer: Hide commands in M-x which do not apply to the current
;; mode. Corfu commands are hidden, since they are not used via M-x. This
;; setting is useful beyond Corfu.
(read-extended-command-predicate #'command-completion-default-include-p))
2025-12-14 23:46:29 +11:00
(use-package cape
2025-12-15 18:20:19 +11:00
:commands (cape-file cape-elisp-block cape-keyword)
:autoload (cape-wrap-noninterruptible cape-wrap-nonexclusive cape-wrap-buster)
:autoload (cape-wrap-silent)
2025-12-14 23:46:29 +11:00
:init
;; Add `completion-at-point-functions', used by `completion-at-point'.
2025-12-16 08:23:57 +11:00
(add-to-list 'completion-at-point-functions #'cape-dabbrev)
2025-12-14 23:46:29 +11:00
(add-to-list 'completion-at-point-functions #'cape-file)
2025-12-15 00:26:46 +11:00
(add-to-list 'completion-at-point-functions #'cape-elisp-block)
(add-to-list 'completion-at-point-functions #'cape-keyword)
;; Make these capfs composable.
(advice-add 'lsp-completion-at-point :around #'cape-wrap-noninterruptible)
(advice-add 'lsp-completion-at-point :around #'cape-wrap-nonexclusive)
(advice-add 'comint-completion-at-point :around #'cape-wrap-nonexclusive)
;; (advice-add 'eglot-completion-at-point :around #'cape-wrap-buster)
;; (advice-add 'eglot-completion-at-point :around #'cape-wrap-nonexclusive)
2025-12-15 00:26:46 +11:00
(advice-add 'pcomplete-completions-at-point :around #'cape-wrap-nonexclusive))
2025-12-14 23:46:29 +11:00
2025-12-14 04:51:24 +11:00
(provide 'init-completion)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; init-completion.el ends here